import { BitcoinError } from "./BitcoinError" ; import { BitcoinErrorCode } from "./BitcoinErrorCode" ; const ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" ; const GENERATOR = [ 0x3b6a57b2 , 0x26508e6d , 0x1ea119fa , 0x3d4233dd , 0x2a1462b3 ] ; export enum Bech32Version { Bech32 = 1 , Bech32m = 0x2bc830a3 } /**
 * Encoding utilities for Bech32 encodings as defined in BIP173:
 * https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#Bech32
 *
 * This encoding uses a human readable part that contain between 1-83
 * US-ASCII characters with char values in the range [33-126].
 *
 * The encoding includes a separator, which is the last "1" in the encoding.
 *
 * Data must be at least 6-characters and is either lowercase or uppercase
 * and excludes the characters "1", "b", "i", and "o".
 */ export class Bech32 { param hrp param words param version returns public static encode ( hrp , words , version = Bech32Version . Bech32 ) { const checksum = createChecksum ( hrp , words , version ) ; const combined = words . concat ( checksum ) ; let result = hrp + "1" ; for ( let i = 0 ; i < combined . length ; i ++ ) { result += ALPHABET [ combined [ i ] ] ; } return result ; } param encoded returns public static decode ( encoded ) { let hasUpper = false ; let hasLower = false ; for ( const char of encoded ) { if ( char >= "A" && char <= "Z" ) hasUpper = true ; if ( char >= "a" && char <= "z" ) hasLower = true ; } if ( hasUpper && hasLower ) { throw new BitcoinError ( BitcoinErrorCode . InvalidBech32Encoding , encoded ) ; } encoded = encoded . toLowerCase ( ) ; const hrpIdx = encoded . lastIndexOf ( "1" ) ; if ( hrpIdx < 1 ) { throw new BitcoinError ( BitcoinErrorCode . InvalidBech32Hrp , encoded ) ; } if ( hrpIdx + 7 > encoded . length ) { throw new BitcoinError ( BitcoinErrorCode . InvalidBech32Hrp , encoded ) ; } const hrp = encoded . substring ( 0 , hrpIdx ) ; for ( const char of hrp ) { const code = char . charCodeAt ( 0 ) ; if ( code < 33 || code > 126 ) { throw new BitcoinError ( BitcoinErrorCode . InvalidBech32Hrp , encoded ) ; } } const words = [ ] ; for ( let i = hrpIdx + 1 ; i < encoded . length ; i ++ ) { const word = ALPHABET . indexOf ( encoded [ i ] ) ; if ( word === - 1 ) { throw new BitcoinError ( BitcoinErrorCode . InvalidBech32Encoding , encoded ) ; } words . push ( word ) ; } const checksum = calculateChecksum ( hrp , words ) ; if ( checksum !== Bech32Version . Bech32 && checksum !== Bech32Version . Bech32m ) { throw new BitcoinError ( BitcoinErrorCode . InvalidBech32Checksum , encoded ) ; } return { hrp , words : words . slice ( 0 , words . length - 6 ) , version : checksum , } ; } param words param pad returns public static wordsToBuffer ( words , pad ) { return Buffer . from ( convertWords ( words , 5 , 8 , pad ) ) ; } param buffer param pad returns public static bufferToWords ( buffer , pad ) { return convertWords ( buffer , 8 , 5 , pad ) ; } param num returns public static sizeofNum ( num ) { if ( num === 0 ) return 1 ; return Math . floor ( Math . log ( num ) / Math . log ( 2 ) / 5 ) + 1 ; } param bits returns public static sizeofBits ( bits ) { return Math . ceil ( bits / 5 ) ; } param bytes returns public static sizeofBytes ( bytes ) { return Bech32 . sizeofBits ( bytes * 8 ) ; } } param data param inBits param outBits param pad return function convertWords ( data , inBits , outBits , pad ) { let value = 0 ; let bits = 0 ; const maxV = ( 1 << outBits ) - 1 ; const result = [ ] ; for ( let i = 0 ; i < data . length ; ++ i ) { value = ( value << inBits ) | data [ i ] ; bits += inBits ; while ( bits >= outBits ) { bits -= outBits ; result . push ( ( value >> bits ) & maxV ) ; } } if ( pad ) { if ( bits > 0 ) { result . push ( ( value << ( outBits - bits ) ) & maxV ) ; } } else { if ( bits >= inBits ) { throw new BitcoinError ( BitcoinErrorCode . InvalidBech32Encoding , { data , inBits , outBits , pad , reason : "excess padding found" , } ) ; } if ( ( value << ( outBits - bits ) ) & maxV ) { throw new BitcoinError ( BitcoinErrorCode . InvalidBech32Encoding , { data , inBits , outBits , pad , reason : "failed to encode all data" , } ) ; } } return result ; } function polymod ( values ) { let chk = 1 ; for ( const value of values ) { const top = chk >> 25 ; chk = ( ( chk & 0x1ffffff ) << 5 ) ^ value ; for ( let i = 0 ; i < 5 ; i ++ ) { if ( ( top >> i ) & 1 ) { chk ^= GENERATOR [ i ] ; } } } return chk ; } function hrpExpand ( hrp ) { const results = [ ] ; for ( let i = 0 ; i < hrp . length ; i ++ ) { results . push ( hrp . charCodeAt ( i ) >> 5 ) ; } results . push ( 0 ) ; for ( let i = 0 ; i < hrp . length ; i ++ ) { results . push ( hrp . charCodeAt ( i ) & 31 ) ; } return results ; } function calculateChecksum ( hrp , data ) { const combined = hrpExpand ( hrp ) . concat ( data ) ; return polymod ( combined ) ; } function createChecksum ( hrp , data , constant ) { const values = hrpExpand ( hrp ) . concat ( data ) . concat ( [ 0 , 0 , 0 , 0 , 0 , 0 ] ) ; const mod = polymod ( values ) ^ constant ; const results = [ ] ; for ( let i = 0 ; i < 6 ; i ++ ) { results . push ( ( mod >> ( 5 * ( 5 - i ) ) ) & 31 ) ; } return results ; }