Address Verification
To perform address verification, OP_NET provides the AddressVerificator class. This class offers type detection, format validation, and support for both classical Bitcoin addresses and quantum-resistant formats.
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.
import { AddressVerificator, MLDSASecurityLevel } from '@btc-vision/transaction';
// Valid ML-DSA-44 public key (1312 bytes)
const level2Key = Buffer.alloc(1312);
const level2Check = AddressVerificator.isValidMLDSAPublicKey(level2Key);
console.log('LEVEL2 valid:', level2Check); // MLDSASecurityLevel.LEVEL2
// Valid ML-DSA-65 public key (1952 bytes)
const level3Key = Buffer.alloc(1952);
const level3Check = AddressVerificator.isValidMLDSAPublicKey(level3Key);
console.log('LEVEL3 valid:', level3Check); // MLDSASecurityLevel.LEVEL3
// Valid ML-DSA-87 public key (2592 bytes)
const level5Key = Buffer.alloc(2592);
const level5Check = AddressVerificator.isValidMLDSAPublicKey(level5Key);
console.log('LEVEL5 valid:', level5Check); // MLDSASecurityLevel.LEVEL5
// Invalid length
const invalidKey = Buffer.alloc(1000);
const invalidCheck = AddressVerificator.isValidMLDSAPublicKey(invalidKey);
console.log('Invalid:', invalidCheck); // nullSupported Input Format
This method accepts the following input formats:
- Hexadecimal string with 0x prefix.
- Hexadecimal string without prefix.
- Buffer.
- Uint8Array.
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
Validating Wallet Keys
This method can also be used to validate a wallet's quantum public 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
// 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
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); // trueValidating 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.
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); // trueAddress 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.
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
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.P2PKHDistinguishing 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
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); // nullComplete 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