UNPKG

@btc-vision/transaction

Version:

OPNet transaction library allows you to create and sign transactions for the OPNet network.

498 lines (405 loc) 17.8 kB
import { describe, expect, it } from 'vitest'; import { AddressTypes, AddressVerificator, MLDSASecurityLevel, Mnemonic } from '../build/opnet.js'; import { networks } from '@btc-vision/bitcoin'; describe('AddressVerificator ML-DSA Support', () => { const testMnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'; describe('isValidMLDSAPublicKey', () => { it('should validate ML-DSA-44 (Level 2) public key from hex string', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const publicKeyHex = wallet.quantumPublicKeyHex; const securityLevel = AddressVerificator.isValidMLDSAPublicKey(publicKeyHex); expect(securityLevel).toBe(MLDSASecurityLevel.LEVEL2); }); it('should validate ML-DSA-44 (Level 2) public key from hex string with 0x prefix', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const publicKeyHex = '0x' + wallet.quantumPublicKeyHex; const securityLevel = AddressVerificator.isValidMLDSAPublicKey(publicKeyHex); expect(securityLevel).toBe(MLDSASecurityLevel.LEVEL2); }); it('should validate ML-DSA-44 (Level 2) public key from Buffer', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const publicKeyBuffer = wallet.quantumPublicKey; const securityLevel = AddressVerificator.isValidMLDSAPublicKey(publicKeyBuffer); expect(securityLevel).toBe(MLDSASecurityLevel.LEVEL2); }); it('should validate ML-DSA-44 (Level 2) public key from Uint8Array', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const publicKeyArray = new Uint8Array(wallet.quantumPublicKey); const securityLevel = AddressVerificator.isValidMLDSAPublicKey(publicKeyArray); expect(securityLevel).toBe(MLDSASecurityLevel.LEVEL2); }); it('should validate ML-DSA-65 (Level 3) public key', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL3, ); const wallet = mnemonic.derive(0); const publicKeyHex = wallet.quantumPublicKeyHex; const securityLevel = AddressVerificator.isValidMLDSAPublicKey(publicKeyHex); expect(securityLevel).toBe(MLDSASecurityLevel.LEVEL3); }); it('should validate ML-DSA-65 (Level 3) public key from Buffer', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL3, ); const wallet = mnemonic.derive(0); const publicKeyBuffer = wallet.quantumPublicKey; const securityLevel = AddressVerificator.isValidMLDSAPublicKey(publicKeyBuffer); expect(securityLevel).toBe(MLDSASecurityLevel.LEVEL3); }); it('should validate ML-DSA-87 (Level 5) public key', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL5, ); const wallet = mnemonic.derive(0); const publicKeyHex = wallet.quantumPublicKeyHex; const securityLevel = AddressVerificator.isValidMLDSAPublicKey(publicKeyHex); expect(securityLevel).toBe(MLDSASecurityLevel.LEVEL5); }); it('should validate ML-DSA-87 (Level 5) public key from Buffer', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL5, ); const wallet = mnemonic.derive(0); const publicKeyBuffer = wallet.quantumPublicKey; const securityLevel = AddressVerificator.isValidMLDSAPublicKey(publicKeyBuffer); expect(securityLevel).toBe(MLDSASecurityLevel.LEVEL5); }); it('should reject invalid hex string', () => { const invalidHex = 'not a valid hex string'; const securityLevel = AddressVerificator.isValidMLDSAPublicKey(invalidHex); expect(securityLevel).toBeNull(); }); it('should reject public key with wrong length', () => { const wrongLength = 'a'.repeat(100); const securityLevel = AddressVerificator.isValidMLDSAPublicKey(wrongLength); expect(securityLevel).toBeNull(); }); it('should reject empty string', () => { const securityLevel = AddressVerificator.isValidMLDSAPublicKey(''); expect(securityLevel).toBeNull(); }); it('should reject empty Buffer', () => { const securityLevel = AddressVerificator.isValidMLDSAPublicKey(Buffer.alloc(0)); expect(securityLevel).toBeNull(); }); it('should reject classical public key (33 bytes)', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const classicalPublicKey = wallet.publicKey; // 33 bytes const securityLevel = AddressVerificator.isValidMLDSAPublicKey(classicalPublicKey); expect(securityLevel).toBeNull(); }); it('should verify all valid ML-DSA lengths', () => { // Test all three security levels const levels = [ MLDSASecurityLevel.LEVEL2, MLDSASecurityLevel.LEVEL3, MLDSASecurityLevel.LEVEL5, ]; for (const level of levels) { const mnemonic = new Mnemonic(testMnemonic, '', networks.bitcoin, level); const wallet = mnemonic.derive(0); const securityLevelHex = AddressVerificator.isValidMLDSAPublicKey( wallet.quantumPublicKeyHex, ); const securityLevelBuffer = AddressVerificator.isValidMLDSAPublicKey( wallet.quantumPublicKey, ); expect(securityLevelHex).toBe(level); expect(securityLevelBuffer).toBe(level); } }); }); describe('isValidP2OPAddress', () => { it('should validate mainnet P2OP address', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.bitcoin); const isValidLegacyPublicKey = AddressVerificator.isValidP2OPAddress( p2opAddress, networks.bitcoin, ); expect(isValidLegacyPublicKey).toBe(true); }); it('should validate testnet P2OP address', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.testnet, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.testnet); const isValidLegacyPublicKey = AddressVerificator.isValidP2OPAddress( p2opAddress, networks.testnet, ); expect(isValidLegacyPublicKey).toBe(true); }); it('should validate regtest P2OP address', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.regtest, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.regtest); const isValidLegacyPublicKey = AddressVerificator.isValidP2OPAddress( p2opAddress, networks.regtest, ); expect(isValidLegacyPublicKey).toBe(true); }); it('should reject P2TR address as P2OP', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2trAddress = wallet.p2tr; const isValidLegacyPublicKey = AddressVerificator.isValidP2OPAddress( p2trAddress, networks.bitcoin, ); expect(isValidLegacyPublicKey).toBe(false); }); it('should reject P2WPKH address as P2OP', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2wpkhAddress = wallet.p2wpkh; const isValidLegacyPublicKey = AddressVerificator.isValidP2OPAddress( p2wpkhAddress, networks.bitcoin, ); expect(isValidLegacyPublicKey).toBe(false); }); it('should reject invalid address string', () => { const invalidAddress = 'not a valid address'; const isValidLegacyPublicKey = AddressVerificator.isValidP2OPAddress( invalidAddress, networks.bitcoin, ); expect(isValidLegacyPublicKey).toBe(false); }); it('should reject empty string', () => { const isValidLegacyPublicKey = AddressVerificator.isValidP2OPAddress( '', networks.bitcoin, ); expect(isValidLegacyPublicKey).toBe(false); }); it('should reject address on wrong network', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.bitcoin); // Try to validate mainnet address on testnet const isValidLegacyPublicKey = AddressVerificator.isValidP2OPAddress( p2opAddress, networks.testnet, ); expect(isValidLegacyPublicKey).toBe(false); }); }); describe('detectAddressType - P2OP support', () => { it('should detect mainnet P2OP address type', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.bitcoin); const addressType = AddressVerificator.detectAddressType(p2opAddress, networks.bitcoin); expect(addressType).toBe(AddressTypes.P2OP); }); it('should detect testnet P2OP address type', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.testnet, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.testnet); const addressType = AddressVerificator.detectAddressType(p2opAddress, networks.testnet); expect(addressType).toBe(AddressTypes.P2OP); }); it('should detect regtest P2OP address type', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.regtest, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.regtest); const addressType = AddressVerificator.detectAddressType(p2opAddress, networks.regtest); expect(addressType).toBe(AddressTypes.P2OP); }); it('should not confuse P2OP with P2TR', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.bitcoin); const p2trAddress = wallet.p2tr; const p2opType = AddressVerificator.detectAddressType(p2opAddress, networks.bitcoin); const p2trType = AddressVerificator.detectAddressType(p2trAddress, networks.bitcoin); expect(p2opType).toBe(AddressTypes.P2OP); expect(p2trType).toBe(AddressTypes.P2TR); expect(p2opType).not.toBe(p2trType); }); it('should not confuse P2OP with P2WPKH', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.bitcoin); const p2wpkhAddress = wallet.p2wpkh; const p2opType = AddressVerificator.detectAddressType(p2opAddress, networks.bitcoin); const p2wpkhType = AddressVerificator.detectAddressType( p2wpkhAddress, networks.bitcoin, ); expect(p2opType).toBe(AddressTypes.P2OP); expect(p2wpkhType).toBe(AddressTypes.P2WPKH); expect(p2opType).not.toBe(p2wpkhType); }); it('should detect P2OP for different security levels', () => { const levels = [ MLDSASecurityLevel.LEVEL2, MLDSASecurityLevel.LEVEL3, MLDSASecurityLevel.LEVEL5, ]; for (const level of levels) { const mnemonic = new Mnemonic(testMnemonic, '', networks.bitcoin, level); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.bitcoin); const addressType = AddressVerificator.detectAddressType( p2opAddress, networks.bitcoin, ); expect(addressType).toBe(AddressTypes.P2OP); } }); it('should return null for invalid P2OP address on wrong network', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2opAddress = wallet.address.p2op(networks.bitcoin); // Try to detect mainnet address on testnet const addressType = AddressVerificator.detectAddressType(p2opAddress, networks.testnet); expect(addressType).toBeNull(); }); }); describe('ML-DSA integration with other address types', () => { it('should correctly identify different address types from same wallet', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const p2trType = AddressVerificator.detectAddressType(wallet.p2tr, networks.bitcoin); const p2wpkhType = AddressVerificator.detectAddressType( wallet.p2wpkh, networks.bitcoin, ); const p2opType = AddressVerificator.detectAddressType( wallet.address.p2op(networks.bitcoin), networks.bitcoin, ); expect(p2trType).toBe(AddressTypes.P2TR); expect(p2wpkhType).toBe(AddressTypes.P2WPKH); expect(p2opType).toBe(AddressTypes.P2OP); }); it('should validate classical and ML-DSA public keys separately', () => { const mnemonic = new Mnemonic( testMnemonic, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2, ); const wallet = mnemonic.derive(0); const classicalValid = AddressVerificator.isValidPublicKey( wallet.toPublicKeyHex(), networks.bitcoin, ); const mldsaLevel = AddressVerificator.isValidMLDSAPublicKey(wallet.quantumPublicKeyHex); expect(classicalValid).toBe(true); expect(mldsaLevel).toBe(MLDSASecurityLevel.LEVEL2); }); }); });