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.

ML-DSA Public Key Validation

Validating ML-DSA Public Keys

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

Supported Input Format

This method accepts the following input formats:

  • Hexadecimal string with 0x prefix.
  • Hexadecimal string without prefix.
  • Buffer.
  • Uint8Array.
typescript
How to use isValidMLDSAPublicKey()
import { AddressVerificator, MLDSASecurityLevel } from '@btc-vision/transaction';

// Hex string (with 0x prefix)
const hexWith0x = '0x' + 'a'.repeat(2624);  // 1312 bytes in hex
const check1 = AddressVerificator.isValidMLDSAPublicKey(hexWith0x);
console.log('Hex with 0x:', check1);  // MLDSASecurityLevel.LEVEL2

// Hex string (without 0x prefix)
const hexWithout0x = 'a'.repeat(2624);
const check2 = AddressVerificator.isValidMLDSAPublicKey(hexWithout0x);
console.log('Hex without 0x:', check2);  // MLDSASecurityLevel.LEVEL2

// Buffer
const buffer = Buffer.alloc(1312);
const check3 = AddressVerificator.isValidMLDSAPublicKey(buffer);
console.log('Buffer:', check3);  // MLDSASecurityLevel.LEVEL2

// Uint8Array
const uint8Array = new Uint8Array(1312);
const check4 = AddressVerificator.isValidMLDSAPublicKey(uint8Array);
console.log('Uint8Array:', check4);  // MLDSASecurityLevel.LEVEL2

// Invalid length
const invalidKey = Buffer.alloc(1000);
const invalidCheck = AddressVerificator.isValidMLDSAPublicKey(invalidKey);
console.log('Invalid:', invalidCheck);  // null

Validating Wallet Keys

This method can also be used to validate a wallet's quantum public key.

typescript
Use isValidMLDSAPublicKey() to validate a quantum wallet key
import { Mnemonic, AddressVerificator, MLDSASecurityLevel } from '@btc-vision/transaction';
import { networks } from '@btc-vision/bitcoin';

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

// Validate quantum public key
const quantumKeyHex = wallet.quantumPublicKeyHex;
const securityLevel = AddressVerificator.isValidMLDSAPublicKey(quantumKeyHex);

console.log('Quantum key valid:', securityLevel !== null);
console.log('Security level:', securityLevel);  // MLDSASecurityLevel.LEVEL2
console.log('Expected:', wallet.securityLevel);  // MLDSASecurityLevel.LEVEL2
console.log('Match:', securityLevel === wallet.securityLevel);  // true
        

Error Cases

typescript
Shows when isValidMLDSAPublicKey() fails and return null
// Empty string
console.log(AddressVerificator.isValidMLDSAPublicKey(''));  // null

// Empty Buffer
console.log(AddressVerificator.isValidMLDSAPublicKey(Buffer.alloc(0)));  // null

// Invalid hex
console.log(AddressVerificator.isValidMLDSAPublicKey('not hex'));  // null

// Wrong length (classical key size)
const classicalKey = Buffer.alloc(33);
console.log(AddressVerificator.isValidMLDSAPublicKey(classicalKey));  // null

// Wrong length (arbitrary size)
const wrongSize = Buffer.alloc(1500);
console.log(AddressVerificator.isValidMLDSAPublicKey(wrongSize));  // null
        

Classical Public Key Validation

Use the isValidPublicKey() method to validate classical public keys. This method returns true if the key is valid, or false otherwise.

Validating Classical Public Keys

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

const mnemonic = Mnemonic.generate();

const wallet = mnemonic.derive(0);

// Validate compressed public key (33 bytes)
const compressedKey = wallet.toPublicKeyHex();
const isValid = AddressVerificator.isValidPublicKey(compressedKey, networks.bitcoin);
console.log('Compressed key valid:', isValid);  // true

// Validate uncompressed public key (65 bytes)
const uncompressedKey = wallet.toUncompressedPublicKey().toString('hex');
const isUncompressedValid = AddressVerificator.isValidPublicKey(uncompressedKey, networks.bitcoin);
console.log('Uncompressed key valid:', isUncompressedValid);  // true

Validating Other Address Types

The AddressVerificator class provides additional validation methods for each supported address type. Use these methods to verify that an address matches a specific format.

See AddressVerificator.ts source code for the AddressVerificator class definition.

typescript
How to use multiple validation methods
import { Mnemonic, AddressVerificator } from '@btc-vision/transaction';
import { networks } from '@btc-vision/bitcoin';

const mnemonic = Mnemonic.generate();

const wallet = mnemonic.derive(0);

// P2TR validation
const p2tr = wallet.p2tr;
const isP2TRValid = AddressVerificator.isValidP2TRAddress(p2tr, networks.bitcoin);
console.log('P2TR valid:', isP2TRValid);  // true

// P2WPKH validation
const p2wpkh = wallet.p2wpkh;
const isP2WPKHValid = AddressVerificator.isP2WPKHAddress(p2wpkh, networks.bitcoin);
console.log('P2WPKH valid:', isP2WPKHValid);  // true

// P2PKH or P2SH validation
const p2pkh = wallet.p2pkh;
const isLegacyValid = AddressVerificator.isP2PKHOrP2SH(p2pkh, networks.bitcoin);
console.log('Legacy valid:', isLegacyValid);  // true

Address Type Detection

Use the detectAddressType() method to identify the type of a given address. This method returns the corresponding AddressTypes enum value, or null if no known address type is found.

Available Address Types

The AddressTypes enum defines all supported address formats in OP_NET.

typescript
AddressTypes enum
enum AddressTypes {
    P2PKH = 'P2PKH',                      // Legacy (1...)
    P2SH_OR_P2SH_P2WPKH = 'P2SH_OR_P2SH-P2WPKH',  // Script hash (3...)
    P2PK = 'P2PK',                        // Public key
    P2TR = 'P2TR',                        // Taproot (bc1p...)
    P2WPKH = 'P2WPKH',                    // SegWit (bc1q...)
    P2WSH = 'P2WSH',                      // SegWit script (bc1q...)
    P2WDA = 'P2WDA',                      // Witness data auth
}

Detecting Address Types

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

const mnemonic = Mnemonic.generate();

const wallet = mnemonic.derive(0);

// P2TR Detection
const p2tr = wallet.p2tr;
const p2trType = AddressVerificator.detectAddressType(p2tr, networks.bitcoin);
console.log('P2TR type:', p2trType);  // AddressTypes.P2TR

// P2WPKH Detection
const p2wpkh = wallet.p2wpkh;
const p2wpkhType = AddressVerificator.detectAddressType(p2wpkh, networks.bitcoin);
console.log('P2WPKH type:', p2wpkhType);  // AddressTypes.P2WPKH

// P2PKH Detection
const p2pkh = wallet.p2pkh;
const p2pkhType = AddressVerificator.detectAddressType(p2pkh, networks.bitcoin);
console.log('P2PKH type:', p2pkhType);  // AddressTypes.P2PKH

Distinguishing Similar Addresses

typescript
How to distinguish similar addresses
import { Mnemonic, AddressVerificator, AddressTypes } from '@btc-vision/transaction';
import { networks } from '@btc-vision/bitcoin';

const mnemonic = Mnemonic.generate();

const wallet = mnemonic.derive(0);

// P2TR vs P2WPKH (both Bech32 formats)
const p2tr = wallet.p2tr;
const p2wpkh = wallet.p2wpkh;

const p2trType = AddressVerificator.detectAddressType(p2tr, networks.bitcoin);
const p2wpkhType = AddressVerificator.detectAddressType(p2wpkh, networks.bitcoin);

console.log('P2TR detected as:', p2trType);  // AddressTypes.P2TR
console.log('P2WPKH detected as:', p2wpkhType);  // AddressTypes.P2WPKH
console.log('Different types:', p2trType !== p2wpkhType);  // true
        

Network-Specific Detection

typescript
How to do network-specific detection
import { Mnemonic, AddressVerificator, AddressTypes } from '@btc-vision/transaction';
import { networks } from '@btc-vision/bitcoin';

const mnemonic = Mnemonic.generate();

const wallet = mnemonic.derive(0);

// Mainnet address on mainnet
const mainnetAddr = wallet.p2tr;
const mainnetDetect = AddressVerificator.detectAddressType(mainnetAddr, networks.bitcoin);
console.log('Mainnet on mainnet:', mainnetDetect);  // AddressTypes.P2TR

// Mainnet address on wrong network
const wrongNetwork = AddressVerificator.detectAddressType(mainnetAddr, networks.testnet);
console.log('Mainnet on testnet:', wrongNetwork);  // null

Complete Validation Example

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

// 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