Address Verification

For address verification, OP_NET provides the AddressVerificator class. This utility validates address format correctness, detects the address type, and supports both classical Bitcoin address formats and OP_NET's quantum-resistant encoding.

Refers to the AddressVerificator Reference section for technical information about the AddressVerificator class.

ML-DSA Public Key Validation

Use the isValidMLDSAPublicKey() method to validate ML-DSA public keys. This method returns the corresponding MLDSASecurityLevel value, or null if the key is invalid.

typescript
How to use isValidMLDSAPublicKey()
import { AddressVerificator } from '@btc-vision/transaction';
import { MLDSASecurityLevel } from '@btc-vision/bip32';

const level = AddressVerificator.isValidMLDSAPublicKey(somePublicKeyHex);
if (level === MLDSASecurityLevel.LEVEL2) {
    console.log('ML-DSA-44 public key (1312 bytes)');
} else if (level === MLDSASecurityLevel.LEVEL3) {
    console.log('ML-DSA-65 public key (1952 bytes)');
} else if (level === MLDSASecurityLevel.LEVEL5) {
    console.log('ML-DSA-87 public key (2592 bytes)');
} else {
    console.log('Not a valid ML-DSA public key');
}

Validating User Input

The detectAddressType() method enables routing logic based on address format. The following example demonstrates validating user input and returning a descriptive message for each supported address type, with proper error handling for unrecognized formats.

typescript
How to use detectAddressType()
import { AddressVerificator, AddressTypes } from '@btc-vision/transaction';
import { networks } from '@btc-vision/bitcoin';

function validateUserAddress(input: string): string {
    const network = networks.bitcoin;
    const type = AddressVerificator.detectAddressType(input, network);

    if (type === null) {
        throw new Error('Unrecognized address format');
    }

    switch (type) {
        case AddressTypes.P2TR:
            return `Valid Taproot address: ${input}`;
        case AddressTypes.P2MR:
            return `Valid P2MR (BIP 360) address: ${input}`;
        case AddressTypes.P2WPKH:
            return `Valid native SegWit address: ${input}`;
        case AddressTypes.P2PKH:
            return `Valid legacy address: ${input}`;
        case AddressTypes.P2SH_OR_P2SH_P2WPKH:
            return `Valid P2SH or nested SegWit address: ${input}`;
        case AddressTypes.P2WSH:
            return `Valid P2WSH address: ${input}`;
        case AddressTypes.P2OP:
            return `Valid OPNet address: ${input}`;
        case AddressTypes.P2PK:
            return `Valid public key (P2PK): ${input}`;
        default:
            throw new Error(`Unsupported address type: ${type}`);
    }
}

Checking all Address Types in a Batch

When processing multiple addresses, individual validation methods can be used to check each format explicitly. The following example iterates through an array of addresses and reports which types each address matches.

typescript
How to use detectAddressType()
const addresses = ['bc1p...', 'bc1q...', '1A1z...', '3J98...'];

for (const addr of addresses) {
    const isP2TR   = AddressVerificator.isValidP2TRAddress(addr, networks.bitcoin);
    const isP2WPKH = AddressVerificator.isP2WPKHAddress(addr, networks.bitcoin);
    const isLegacy = AddressVerificator.isP2PKHOrP2SH(addr, networks.bitcoin);

    console.log(`${addr}: P2TR=${isP2TR}, P2WPKH=${isP2WPKH}, Legacy=${isLegacy}`);
}

Complete Validation Example

typescript
Extensive address validation example
import {
    Mnemonic,
    AddressVerificator,
    AddressTypes
} from '@btc-vision/transaction';
import { networks } from '@btc-vision/bitcoin';
import { MLDSASecurityLevel } from '@btc-vision/bip32';

// Generate wallet
const mnemonic = Mnemonic.generate(undefined, '', networks.bitcoin, MLDSASecurityLevel.LEVEL2);
const wallet = mnemonic.derive(0);

console.log('=== ML-DSA Public Key Validation ===');

// Validate quantum public key
const quantumKeyHex = wallet.quantumPublicKeyHex;
const quantumKeyBuffer = wallet.quantumPublicKey;

const securityLevelFromHex = AddressVerificator.isValidMLDSAPublicKey(quantumKeyHex);
const securityLevelFromBuffer = AddressVerificator.isValidMLDSAPublicKey(quantumKeyBuffer);

console.log('Hex validation:', securityLevelFromHex);  // MLDSASecurityLevel.LEVEL2
console.log('Buffer validation:', securityLevelFromBuffer);  // MLDSASecurityLevel.LEVEL2
console.log('Matches wallet:', securityLevelFromHex === wallet.securityLevel);  // true

console.log('\n=== Address Type Detection ===');

// Detect all address types
const addresses = {
    p2tr: wallet.p2tr,
    p2wpkh: wallet.p2wpkh,
    p2pkh: wallet.p2pkh,
};

for (const [name, addr] of Object.entries(addresses)) {
    const type = AddressVerificator.detectAddressType(addr, networks.bitcoin);
    console.log(`${name}: ${type}`);
}

console.log('\n=== Classical Key Validation ===');

// Validate classical public key
const classicalKey = wallet.toPublicKeyHex();
const isClassicalValid = AddressVerificator.isValidPublicKey(classicalKey, networks.bitcoin);

console.log('Classical key:', classicalKey);
console.log('Classical valid:', isClassicalValid);  // true

console.log('\n=== Cross-Network Validation ===');

// Test network mismatch
const mainnetP2TR = wallet.p2tr;
const testnetMnemonic = Mnemonic.generate(undefined, '', networks.testnet, MLDSASecurityLevel.LEVEL2);
const testnetWallet = testnetMnemonic.derive(0);
const testnetP2TR = testnetWallet.p2tr;

const mainnetType = AddressVerificator.detectAddressType(mainnetP2TR, networks.bitcoin);
const wrongNetworkType = AddressVerificator.detectAddressType(mainnetP2TR, networks.testnet);

console.log('Mainnet P2TR on mainnet:', mainnetType);  // AddressTypes.P2TR
console.log('Mainnet P2TR on testnet:', wrongNetworkType);  // null

console.log('\n=== Complete Wallet Validation ===');

function validateWallet(wallet: any, network: any): boolean {
    // Validate quantum key
    const quantumValid = AddressVerificator.isValidMLDSAPublicKey(
        wallet.quantumPublicKey
    ) !== null;

    // Validate classical key
    const classicalValid = AddressVerificator.isValidPublicKey(
        wallet.toPublicKeyHex(),
        network
    );

    return quantumValid && classicalValid;
}

const isWalletValid = validateWallet(wallet, networks.bitcoin);
console.log('Complete wallet validation:', isWalletValid);  // true