UNPKG

opnet

Version:

The perfect library for building Bitcoin-based applications.

487 lines (369 loc) 11.8 kB
# Public Key Operations This guide covers public key lookups and address operations on OPNet. ## Overview OPNet provides methods to look up public key information for addresses, including unified address resolution across different Bitcoin address formats and ML-DSA quantum-resistant key support. ```mermaid flowchart LR A[Address] --> B[Provider] B --> C{Method} C --> D[getPublicKeyInfo] C --> E[getPublicKeysInfo] C --> F[getCSV1ForAddress] C --> G[validateAddress] D --> H[Address Object] E --> I[AddressesInfo Map] ``` --- ## Get Public Key Info (Single) ### Basic Query ```typescript import { JSONRpcProvider } from 'opnet'; import { networks, Address } from '@btc-vision/bitcoin'; const network = networks.regtest; const provider = new JSONRpcProvider({ url: 'https://regtest.opnet.org', network }); // Get public key info for an address const address = await provider.getPublicKeyInfo( 'bc1p...taproot-address...', false // Not a contract ); console.log('Address:', address.toHex()); console.log('P2TR:', address.p2tr(network)); ``` ### For Contract Addresses ```typescript // For contract addresses, set isContract to true const contractAddress = await provider.getPublicKeyInfo( 'bc1p...contract-address...', true // Is a contract ); console.log('Contract public key:', contractAddress.toHex()); ``` ### Method Signature ```typescript async getPublicKeyInfo( addressRaw: string | Address, // Address to look up isContract: boolean // Whether address is a contract ): Promise<Address> ``` --- ## Get Public Keys Info (Multiple) ### Batch Query ```typescript const addresses = [ 'bc1p...address1...', 'bc1p...address2...', 'bc1q...address3...', ]; const results = await provider.getPublicKeysInfo( addresses, false, // Not contracts true // Log errors ); // Results is a map of input -> Address for (const [input, address] of Object.entries(results)) { console.log(`${input} => ${address.toHex()}`); } ``` ### With Error Handling ```typescript const results = await provider.getPublicKeysInfo( addresses, false, true // Enable error logging ); // Check for missing addresses for (const addr of addresses) { if (!(addr in results)) { console.log(`Address not found: ${addr}`); } } ``` ### Method Signature ```typescript async getPublicKeysInfo( addresses: string | string[] | Address | Address[], isContract: boolean = false, logErrors: boolean = false ): Promise<AddressesInfo> ``` --- ## Get Raw Public Key Info ### Raw API Response ```typescript const rawInfo = await provider.getPublicKeysInfoRaw([ 'bc1p...address...', ]); // Raw info contains all address formats for (const [key, info] of Object.entries(rawInfo)) { if ('error' in info) { console.log(`Error for ${key}: ${info.error}`); continue; } console.log(`Public Key Info for ${key}:`); console.log(' Original PubKey:', info.originalPubKey); console.log(' Tweaked PubKey:', info.tweakedPubkey); console.log(' P2TR:', info.p2tr); console.log(' P2OP:', info.p2op); console.log(' P2WPKH:', info.p2wpkh); console.log(' P2PKH:', info.p2pkh); // ML-DSA info (quantum-resistant) if (info.mldsaHashedPublicKey) { console.log(' ML-DSA Level:', info.mldsaLevel); console.log(' ML-DSA Hashed PK:', info.mldsaHashedPublicKey); } } ``` ### Method Signature ```typescript async getPublicKeysInfoRaw( addresses: string | string[] | Address | Address[] ): Promise<IPublicKeyInfoResult> ``` --- ## PublicKeyInfo Structure ```typescript interface PublicKeyInfo { // Public keys originalPubKey?: string; // Original (untweaked) public key tweakedPubkey?: string; // Tweaked public key for Taproot // Address formats p2tr?: string; // Pay-to-Taproot address p2op?: string; // OPNet P2OP address p2pkh?: string; // Pay-to-Public-Key-Hash (legacy) p2pkhUncompressed?: string; // P2PKH with uncompressed key p2pkhHybrid?: string; // P2PKH with hybrid key p2shp2wpkh?: string; // P2SH-wrapped P2WPKH p2wpkh?: string; // Pay-to-Witness-Public-Key-Hash // Internal lowByte?: number; // Low byte of public key // ML-DSA (quantum-resistant) mldsaHashedPublicKey?: string; // Hashed ML-DSA public key mldsaLevel?: MLDSASecurityLevel; // ML-DSA security level mldsaPublicKey?: string | null; // Full ML-DSA public key } ``` --- ## CSV1 Address Lookup ### Get CSV1 Address ```typescript import { Address, toHex } from '@btc-vision/bitcoin'; const address = Address.fromString('bc1p...taproot-address...'); const csv1Address = provider.getCSV1ForAddress(address); console.log('CSV1 Address:', csv1Address.address); console.log('Redeem Script:', csv1Address.redeemScript ? toHex(csv1Address.redeemScript) : undefined); ``` ### Method Signature ```typescript getCSV1ForAddress(address: Address): IP2WSHAddress ``` --- ## Address Validation ### Validate Address Format ```typescript const addressType = provider.validateAddress( 'bc1p...address...', network ); if (addressType === null) { console.log('Invalid address'); } else { console.log('Address type:', addressType); } ``` ### Address Types ```typescript enum AddressTypes { P2PKH = 'P2PKH', // Legacy address (1...) P2OP = 'P2OP', // OPNet contract address P2SH_OR_P2SH_P2WPKH = 'P2SH_OR_P2SH-P2WPKH', // Script hash (3...) P2PK = 'P2PK', // Public key P2TR = 'P2TR', // Taproot (bc1p...) P2MR = 'P2MR', // Pay-to-Merkle-Root / BIP 360 (bc1z...) P2WPKH = 'P2WPKH', // Native SegWit (bc1q...) P2WSH = 'P2WSH', // SegWit script hash // P2WDA = 'P2WDA', // UNUSED - internal only } ``` ### Method Signature ```typescript validateAddress( addr: string | Address, network: Network ): AddressTypes | null ``` --- ## Working with Addresses ### Convert Between Formats ```typescript async function getAddressFormats( provider: JSONRpcProvider, address: string ): Promise<{ p2tr?: string; p2op?: string; p2wpkh?: string; p2pkh?: string; }> { const raw = await provider.getPublicKeysInfoRaw([address]); const info = raw[address]; if ('error' in info) { throw new Error(`Address not found: ${info.error}`); } return { p2tr: info.p2tr, p2op: info.p2op, p2wpkh: info.p2wpkh, p2pkh: info.p2pkh, }; } // Usage const formats = await getAddressFormats(provider, taprootAddress); console.log('P2TR:', formats.p2tr); console.log('P2OP:', formats.p2op); ``` ### Check ML-DSA Support ```typescript async function hasMLDSAKey( provider: JSONRpcProvider, address: string ): Promise<boolean> { const raw = await provider.getPublicKeysInfoRaw([address]); const info = raw[address]; if ('error' in info) { return false; } return info.mldsaHashedPublicKey !== undefined; } // Usage const hasQuantumKey = await hasMLDSAKey(provider, address); console.log('Has ML-DSA key:', hasQuantumKey); ``` --- ## Batch Operations ### Resolve Multiple Addresses ```typescript async function resolveAddresses( provider: JSONRpcProvider, addresses: string[] ): Promise<Map<string, string | null>> { const results = new Map<string, string | null>(); const info = await provider.getPublicKeysInfo(addresses, false, false); for (const addr of addresses) { if (addr in info) { results.set(addr, info[addr].toHex()); } else { results.set(addr, null); } } return results; } // Usage const resolved = await resolveAddresses(provider, [ 'bc1p...addr1...', 'bc1q...addr2...', '1BvBM...addr3...', ]); for (const [addr, pubkey] of resolved) { console.log(`${addr}: ${pubkey ?? 'NOT FOUND'}`); } ``` ### Filter by Address Type ```typescript function filterAddressesByType( provider: JSONRpcProvider, addresses: string[], targetType: AddressTypes ): string[] { return addresses.filter(addr => { const type = provider.validateAddress(addr, provider.network); return type === targetType; }); } // Usage const taprootOnly = filterAddressesByType( provider, allAddresses, AddressTypes.P2TR ); console.log('Taproot addresses:', taprootOnly.length); ``` --- ## Complete Public Key Service ```typescript class PublicKeyService { constructor( private provider: JSONRpcProvider, private network: Network ) {} async getInfo(address: string, isContract: boolean = false): Promise<Address> { return this.provider.getPublicKeyInfo(address, isContract); } async getBatchInfo( addresses: string[], isContract: boolean = false ): Promise<AddressesInfo> { return this.provider.getPublicKeysInfo(addresses, isContract, true); } async getRawInfo(address: string): Promise<PublicKeyInfo | null> { const result = await this.provider.getPublicKeysInfoRaw([address]); const info = result[address]; if ('error' in info) { return null; } return info; } async getAllFormats(address: string): Promise<Record<string, string>> { const info = await this.getRawInfo(address); if (!info) { throw new Error('Address not found'); } const formats: Record<string, string> = {}; if (info.p2tr) formats.p2tr = info.p2tr; if (info.p2op) formats.p2op = info.p2op; if (info.p2wpkh) formats.p2wpkh = info.p2wpkh; if (info.p2pkh) formats.p2pkh = info.p2pkh; if (info.p2shp2wpkh) formats.p2shp2wpkh = info.p2shp2wpkh; return formats; } validate(address: string): AddressTypes | null { return this.provider.validateAddress(address, this.network); } isValid(address: string): boolean { return this.validate(address) !== null; } getCSV1(address: Address): IP2WSHAddress { return this.provider.getCSV1ForAddress(address); } async hasQuantumKey(address: string): Promise<boolean> { const info = await this.getRawInfo(address); return info?.mldsaHashedPublicKey !== undefined; } } // Usage const pkService = new PublicKeyService(provider, network); // Validate address if (pkService.isValid(address)) { console.log('Valid address'); } // Get all formats const formats = await pkService.getAllFormats(address); console.log('Address formats:', formats); // Check quantum key const hasQuantum = await pkService.hasQuantumKey(address); console.log('Has ML-DSA key:', hasQuantum); ``` --- ## Best Practices 1. **Validate First**: Always validate addresses before lookups 2. **Batch Requests**: Use `getPublicKeysInfo` for multiple addresses 3. **Handle Missing**: Not all addresses may have public key info 4. **Cache Results**: Public key info doesn't change, safe to cache 5. **Contract Flag**: Set `isContract: true` for contract addresses 6. **Enable Logging**: Use `logErrors: true` during development --- ## Next Steps - [Bitcoin Utils](../utils/bitcoin-utils.md) - Utility functions - [Revert Decoder](../utils/revert-decoder.md) - Error decoding - [ABI Overview](../abi-reference/abi-overview.md) - ABI system --- [ Previous: Broadcasting](../transactions/broadcasting.md) | [Next: Bitcoin Utils ](../utils/bitcoin-utils.md)