UNPKG

@btc-vision/transaction

Version:

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

464 lines (352 loc) 16.7 kB
# AddressVerificator Provides static utility methods for validating and detecting Bitcoin address types. `AddressVerificator` can identify P2PKH, P2WPKH, P2TR, P2MR, P2WSH, P2SH, P2OP, and P2WDA addresses, validate public keys (both classical and ML-DSA), and determine whether a P2WSH address is actually a P2WDA address by inspecting its witness script. This class is entirely static and is not meant to be instantiated. ## Table of Contents - [Import](#import) - [Enums and Interfaces](#enums-and-interfaces) - [AddressTypes](#addresstypes) - [ValidatedP2WDAAddress](#validatedp2wdaaddress) - [Taproot Validation](#taproot-validation) - [isValidP2TRAddress()](#isvalidp2traddress) - [SegWit Validation](#segwit-validation) - [isP2WPKHAddress()](#isp2wpkhaddress) - [Legacy Address Validation](#legacy-address-validation) - [isP2PKHOrP2SH()](#isp2pkhorP2SH) - [requireRedeemScript()](#requireredeemscript) - [OPNet Address Validation](#opnet-address-validation) - [isValidP2OPAddress()](#isvalidp2opaddress) - [Public Key Validation](#public-key-validation) - [isValidPublicKey()](#isvalidpublickey) - [isValidMLDSAPublicKey()](#isvalidmldsapublickey) - [P2WDA Validation](#p2wda-validation) - [isP2WDAWitnessScript()](#isp2wdawitnessscript) - [validateP2WDAAddress()](#validatep2wdaaddress) - [Address Type Detection](#address-type-detection) - [detectAddressType()](#detectaddresstype) - [detectAddressTypeWithWitnessScript()](#detectaddresstypewithwitnessscript) - [Code Examples](#code-examples) - [Best Practices](#best-practices) - [Related Documentation](#related-documentation) ## Import ```typescript import { AddressVerificator, AddressTypes } from '@btc-vision/transaction'; import { networks } from '@btc-vision/bitcoin'; ``` ## Enums and Interfaces ### AddressTypes An enum representing all recognized Bitcoin address types. ```typescript enum AddressTypes { P2PKH = 'P2PKH', P2OP = 'P2OP', P2SH_OR_P2SH_P2WPKH = 'P2SH_OR_P2SH-P2WPKH', P2PK = 'P2PK', P2TR = 'P2TR', P2MR = 'P2MR', P2WPKH = 'P2WPKH', P2WSH = 'P2WSH', P2WDA = 'P2WDA', } ``` | Value | Description | |-------|-------------| | `P2PKH` | Legacy Pay-to-Public-Key-Hash (starts with `1`). | | `P2OP` | Pay-to-OPNet (witness version 16, bech32m). | | `P2SH_OR_P2SH_P2WPKH` | Pay-to-Script-Hash or nested SegWit (starts with `3`). Cannot distinguish without the redeem script. | | `P2PK` | Pay-to-Public-Key (detected when input is a valid public key hex). | | `P2TR` | Pay-to-Taproot (witness version 1, 32-byte program, starts with `bc1p`). | | `P2MR` | Pay-to-Merkle-Root / BIP 360 (witness version 2, 32-byte program, starts with `bc1z`). Quantum-safe, no key-path spend. | | `P2WPKH` | Pay-to-Witness-Public-Key-Hash (witness version 0, 20-byte program, starts with `bc1q`). | | `P2WSH` | Pay-to-Witness-Script-Hash (witness version 0, 32-byte program, starts with `bc1q`). | | `P2WDA` | Pay-to-Witness-Data-Authentication (a P2WSH variant with a specific witness script pattern). | ### ValidatedP2WDAAddress The result of a detailed P2WDA address validation. ```typescript interface ValidatedP2WDAAddress { readonly isValid: boolean; readonly isPotentiallyP2WDA: boolean; readonly isDefinitelyP2WDA: boolean; readonly publicKey?: Uint8Array; readonly error?: string; } ``` | Field | Type | Description | |-------|------|-------------| | `isValid` | `boolean` | Whether the base address structure is valid. | | `isPotentiallyP2WDA` | `boolean` | Whether the address could be a P2WDA (is a valid P2WSH). | | `isDefinitelyP2WDA` | `boolean` | Whether the witness script confirms a P2WDA pattern. | | `publicKey` | `Uint8Array \| undefined` | The public key extracted from the P2WDA witness script, if confirmed. | | `error` | `string \| undefined` | Error message if validation failed. | ## Taproot Validation ### isValidP2TRAddress() ```typescript static isValidP2TRAddress(inAddress: string, network: Network): boolean ``` Validates whether a string is a valid Taproot (P2TR) address on the given network. Checks bech32m encoding, witness version 1, and the ability to produce a valid output script. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `inAddress` | `string` | Yes | The address string to validate. | | `network` | `Network` | Yes | The Bitcoin network. | **Returns:** `boolean` ```typescript const isP2TR = AddressVerificator.isValidP2TRAddress('bc1p...', networks.bitcoin); console.log(isP2TR); // true ``` ## SegWit Validation ### isP2WPKHAddress() ```typescript static isP2WPKHAddress(inAddress: string, network: Network): boolean ``` Validates whether a string is a valid native SegWit (P2WPKH) address. Checks bech32 encoding, witness version 0, and 20-byte program length. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `inAddress` | `string` | Yes | The address string to validate. | | `network` | `Network` | Yes | The Bitcoin network. | **Returns:** `boolean` ```typescript const isSegWit = AddressVerificator.isP2WPKHAddress('bc1q...', networks.bitcoin); console.log(isSegWit); // true ``` ## Legacy Address Validation ### isP2PKHOrP2SH() ```typescript static isP2PKHOrP2SH(addy: string, network: Network): boolean ``` Validates whether a string is a valid P2PKH or P2SH address (base58check encoded). | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `addy` | `string` | Yes | The address string to validate. | | `network` | `Network` | Yes | The Bitcoin network. | **Returns:** `boolean` -- `true` if the address decodes as either P2PKH or P2SH on the given network. ```typescript const isLegacy = AddressVerificator.isP2PKHOrP2SH('1A1zP1...', networks.bitcoin); console.log(isLegacy); // true ``` ### requireRedeemScript() ```typescript static requireRedeemScript(addy: string, network: Network): boolean ``` Determines whether the address is a P2SH address that requires a redeem script for spending. Returns `false` for P2PKH addresses. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `addy` | `string` | Yes | The address string to check. | | `network` | `Network` | Yes | The Bitcoin network. | **Returns:** `boolean` -- `true` if the address is P2SH (requires a redeem script), `false` otherwise. ## OPNet Address Validation ### isValidP2OPAddress() ```typescript static isValidP2OPAddress(inAddress: string, network: Network): boolean ``` Validates whether a string is a valid P2OP (Pay-to-OPNet) address. Checks for witness version 16 and a 21-byte witness program. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `inAddress` | `string` | Yes | The address string to validate. | | `network` | `Network` | Yes | The Bitcoin network. | **Returns:** `boolean` ```typescript const isP2OP = AddressVerificator.isValidP2OPAddress('bc1s...', networks.bitcoin); console.log(isP2OP); // true or false ``` ## Public Key Validation ### isValidPublicKey() ```typescript static isValidPublicKey(input: string, network: Network): boolean ``` Validates whether a hex string represents a valid classical Bitcoin public key. Supports compressed (33-byte), uncompressed (65-byte), x-only (32-byte), and hybrid (65-byte with prefix `0x06` or `0x07`) formats. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `input` | `string` | Yes | The hex-encoded public key (with optional `0x` prefix). | | `network` | `Network` | Yes | The Bitcoin network. | **Returns:** `boolean` ```typescript const isValid = AddressVerificator.isValidPublicKey( '0x020373626d317ae8788ce3280b491068610d840c23ecb64c14075bbb9f670af52c', networks.bitcoin, ); console.log(isValid); // true ``` ### isValidMLDSAPublicKey() ```typescript static isValidMLDSAPublicKey(input: string | Uint8Array): MLDSASecurityLevel | null ``` Validates whether the input is a valid ML-DSA public key based on its byte length. Returns the corresponding security level if valid, or `null` if the key length does not match any ML-DSA variant. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `input` | `string \| Uint8Array` | Yes | The public key as hex string (with optional `0x` prefix) or raw bytes. | **Returns:** `MLDSASecurityLevel | null` | Byte Length | Security Level | ML-DSA Variant | |-------------|---------------|----------------| | 1312 | `MLDSASecurityLevel.LEVEL2` | ML-DSA-44 | | 1952 | `MLDSASecurityLevel.LEVEL3` | ML-DSA-65 | | 2592 | `MLDSASecurityLevel.LEVEL5` | ML-DSA-87 | ```typescript const level = AddressVerificator.isValidMLDSAPublicKey('0xaabb...'); if (level !== null) { console.log('Valid ML-DSA key at security level:', level); } else { console.log('Not a valid ML-DSA public key'); } ``` ## P2WDA Validation ### isP2WDAWitnessScript() ```typescript static isP2WDAWitnessScript(witnessScript: Uint8Array): boolean ``` Checks whether a witness script matches the P2WDA pattern: `(OP_2DROP * 5) <pubkey> OP_CHECKSIG`. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `witnessScript` | `Uint8Array` | Yes | The raw witness script bytes. | **Returns:** `boolean` ```typescript const isP2WDA = AddressVerificator.isP2WDAWitnessScript(witnessScript); console.log(isP2WDA); // true if witness script matches P2WDA pattern ``` ### validateP2WDAAddress() ```typescript static validateP2WDAAddress( address: string, network: Network, witnessScript?: Uint8Array, ): ValidatedP2WDAAddress ``` Performs a comprehensive P2WDA address validation with three levels of certainty: 1. **isValid** -- the base address is structurally valid. 2. **isPotentiallyP2WDA** -- the address is a P2WSH (could be P2WDA). 3. **isDefinitelyP2WDA** -- the witness script matches the P2WDA pattern AND produces the given address. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `address` | `string` | Yes | The address to validate. | | `network` | `Network` | Yes | The Bitcoin network. | | `witnessScript` | `Uint8Array` | No | Optional witness script for definitive validation. | **Returns:** `ValidatedP2WDAAddress` ```typescript // Without witness script - can only determine if it's potentially P2WDA const result1 = AddressVerificator.validateP2WDAAddress('bc1q...', networks.bitcoin); console.log(result1.isPotentiallyP2WDA); // true if P2WSH // With witness script - can definitively confirm P2WDA const result2 = AddressVerificator.validateP2WDAAddress('bc1q...', networks.bitcoin, witnessScript); console.log(result2.isDefinitelyP2WDA); // true if confirmed P2WDA console.log(result2.publicKey); // Uint8Array if public key extracted ``` ## Address Type Detection ### detectAddressType() ```typescript static detectAddressType(addy: string, network: Network): AddressTypes | null ``` Detects the type of a Bitcoin address string. Returns `null` if the address type cannot be determined. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `addy` | `string` | Yes | The address or public key string. | | `network` | `Network` | Yes | The Bitcoin network. | **Returns:** `AddressTypes | null` Detection order: 1. Checks if input is a valid public key (`P2PK`). 2. Attempts base58 decode for `P2PKH` or `P2SH_OR_P2SH_P2WPKH`. 3. Attempts bech32 decode for `P2OP`, `P2WPKH`, `P2WSH`, `P2TR`, or `P2MR`. ```typescript const type = AddressVerificator.detectAddressType('bc1p...', networks.bitcoin); console.log(type); // AddressTypes.P2TR const type2 = AddressVerificator.detectAddressType('1A1zP1...', networks.bitcoin); console.log(type2); // AddressTypes.P2PKH const type3 = AddressVerificator.detectAddressType('bc1q...short', networks.bitcoin); console.log(type3); // AddressTypes.P2WPKH (if 20-byte program) or P2WSH (if 32-byte) ``` ### detectAddressTypeWithWitnessScript() ```typescript static detectAddressTypeWithWitnessScript( addy: string, network: Network, witnessScript?: Uint8Array, ): AddressTypes | null ``` Enhanced address type detection that can distinguish P2WDA from regular P2WSH when a witness script is provided. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `addy` | `string` | Yes | The address string. | | `network` | `Network` | Yes | The Bitcoin network. | | `witnessScript` | `Uint8Array` | No | Optional witness script for P2WDA detection. | **Returns:** `AddressTypes | null` If the base type is `P2WSH` and a witness script is provided that matches the P2WDA pattern, returns `AddressTypes.P2WDA` instead of `AddressTypes.P2WSH`. ```typescript // Without witness script, P2WDA addresses show as P2WSH const type1 = AddressVerificator.detectAddressTypeWithWitnessScript( 'bc1q...', networks.bitcoin, ); console.log(type1); // AddressTypes.P2WSH // With witness script, P2WDA is correctly identified const type2 = AddressVerificator.detectAddressTypeWithWitnessScript( 'bc1q...', networks.bitcoin, p2wdaWitnessScript, ); console.log(type2); // AddressTypes.P2WDA ``` ## Code Examples ### Validating user input ```typescript 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 ```typescript 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}`); } ``` ### Validating ML-DSA public keys ```typescript 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'); } ``` ## Best Practices 1. **Always validate addresses before using them** in transactions. Sending funds to an invalid address results in permanent loss. 2. **Use `detectAddressType()` for routing logic** instead of string-prefix heuristics, which can fail across networks. 3. **Provide the witness script when validating P2WDA** to get definitive confirmation rather than just potential P2WDA status. 4. **Validate public keys with `isValidPublicKey()`** before passing them to `Address.fromString()` or multi-sig construction. 5. **Check the network parameter** carefully. An address valid on mainnet may not be valid on testnet and vice versa. ## Related Documentation - [Address](./address.md) -- Quantum-resistant address representation - [EcKeyPair](./ec-keypair.md) -- Classical ECDSA/Schnorr key pair utilities - [Wallet](./wallet.md) -- Manages both classical and quantum-resistant keys - [P2WDA Addresses](../addresses/P2WDA.md) -- Pay-to-Witness-Data-Authentication