import bech32 from "bech32" ; import bs58check from "bs58check" ; import { ADDRESS_VERSION } from "./address-version" ; import { EXPIRY_DEFAULT , MIN_FINAL_CLTV_EXPIRY_DEFAULT } from "./constants" ; import * as crypto from "./crypto" ; import { FallbackAddress } from "./fallback-address" ; import { FIELD_TYPE } from "./field-type" ; import { Route } from "./route" ; import { Signature } from "./signature" ; const picoToMsat = BigInt ( 10 ) ; const picoToSat = picoToMsat * BigInt ( 1000 ) ; const picoToBtc = BigInt ( 1e12 ) ; const MAX_SHORT_DESC_BYTES = 639 ; /**
 * Invoice is the state container used for invoice data. It is used
 * when building an invoice or contains the results from decoded
 * invoices. The Invoice type does not perform validation on
 * data it contains but does contain helper methods to help
 * construct proper invoices.
 */ export class Invoice { public network : string ; public timestamp : number = 0 ; public fields : any [ ] = [ ] ; public unknownFields : any [ ] = [ ] ; /**
     * ECDSA signature used to sign the invoice.
     */ public signature : Signature ; /**
     * Compressed public key on elliptic curve secp256k1 corresponding
     * to the node that generated and signed the invoice. Returned
     * as 33-bytes.
     */ public pubkey : Buffer ; /**
     * Buffer containing the buffer of the data used to generate the
     * hash used in the signature.
     */ public hashData : Buffer ; /**
     * Inidicates if signature recovery was used when decoding and
     * performing signature verification for an invoice. This value
     * will be false if a payee node field was provided.
     */ public usedSigRecovery : boolean ; /**
     * Invoice value stored in pico bitcoin
     */ public _value : bigint ; /**
     * Returns true when the invoice has a value
     * associated with it. Invoices may optionally contain a value.
     * When there is no value, the invoice is for the receipt of
     * any value.
     */ public get hasValue ( ) : boolean { return typeof this . _value === "bigint" ; } deprecated public get amount ( ) : string { return this . hasValue ? ( Number ( this . _value ) / Number ( picoToBtc ) ) . toFixed ( 11 ) : null ; } public set amount ( val ) { if ( ! val ) this . _value = null ; else this . _value = BigInt ( Math . trunc ( parseFloat ( val ) * Number ( picoToBtc ) ) ) ; } /**
     * Warning: Msat fractions are truncated!
     *
     * Gets the value in satoshi as a string by converting from pico btc
     * into satoshi. Returns null if the invoice has no amount.
     *
     * Sets the value in satoshi from a string or number, such as 10000 satoshi.
     * Setting a falsy value will remove the value from the invoice.
     */ public get valueSat ( ) { return this . hasValue ? ( this . _value / picoToSat ) . toString ( ) : null ; } public set valueSat ( val ) { if ( ! val ) this . _value = null ; else this . _value = BigInt ( val ) * picoToSat ; } /**
     * Gets the value in milli-sataoshi as a string or returns null
     * if the invoice has no amount.
     *
     * Sets the value in millisatoshi from a string or number. Setting a falsy
     * value will remove the value from the invoice.
     */ public get valueMsat ( ) : string { return this . hasValue ? ( this . _value / picoToMsat ) . toString ( ) : null ; } public set valueMsat ( val ) { if ( ! val || Number ( val ) === 0 ) this . _value = null ; else this . _value = BigInt ( val ) * picoToMsat ; } /**
     * Get the expiry time for the invoice as a big endian number
     * of seconds. The defualt is one hour (3600).
     *
     * Sets the expiry time in seconds for the invoice.  Only a single
     * expiry field is valid in the invoice.
     */ public get expiry ( ) : number { return this . _getFieldValue ( FIELD_TYPE . EXPIRY , EXPIRY_DEFAULT ) ; } public set expiry ( value ) { this . _setFieldValue ( FIELD_TYPE . EXPIRY , value ) ; } /**
     * Gets the 256-bit payment hash. The preimage of this value
     * will provide proof of payment.
     *
     * Sets the 256-bit payment hash for the invoice from a Buffer
     * or hex-encoded string. Only a single field of this type is
     * valid in the invoice.
     */ public get paymentHash ( ) : Buffer { return this . _getFieldValue ( FIELD_TYPE . PAYMENT_HASH ) ; } public set paymentHash ( value ) { if ( typeof value === "string" ) value = Buffer . from ( value , "hex" ) ; this . _setFieldValue ( FIELD_TYPE . PAYMENT_HASH , value ) ; } /**
     * Gets the description as either a shortDesc or hashDesc
     * value. If it is the former it is returned as a string.
     * hashDesc is returned as a buffer of the hash.
     *
     * Sets the description for the invoice. An invoice must use hash
     * description for messages longer than 639 bytes. If the string is
     * longer than 639 bytes, the description will be hashed and stored
     * in hashDesc. Otherwise, the raw string will be stored in the
     * short desc.
     */ public get desc ( ) : string | Buffer { return this . shortDesc || this . hashDesc ; } public set desc ( desc ) { const len = Buffer . byteLength ( desc ) ; if ( len > MAX_SHORT_DESC_BYTES ) this . hashDesc = crypto . sha256 ( desc as Buffer ) ; else this . shortDesc = desc as string ; } /**
     * Gets the short description text. Returns null when the invoice
     * does not contain a short description. An invoice must set
     * either a short description or a hash description.
     *
     * Sets the short description text. Maximum valid length is 639
     * bytes. Only a single short desc or hash desc field is allowed.
     * Setting this field will remove the hashDesc field value.
     */ public get shortDesc ( ) : string { return this . _getFieldValue ( FIELD_TYPE . SHORT_DESC ) ; } public set shortDesc ( value ) { this . _removeFieldByType ( FIELD_TYPE . HASH_DESC ) ; this . _setFieldValue ( FIELD_TYPE . SHORT_DESC , value ) ; } /**
     * Gets the 256-bit hash of the description. Returns
     * null when an invoice does not contain a hash description.
     * An invoice must contain either a shortDesc or hashDesc.
     *
     * Sets the hash description to the hex-encoded string or
     * Buffer containing the the hashed description.
     * This must be used for descriptions that are over 639 bytes
     * long. Setting this field will remove any short desc fields.
     */ public get hashDesc ( ) : Buffer { return this . _getFieldValue ( FIELD_TYPE . HASH_DESC ) ; } public set hashDesc ( value ) { if ( typeof value === "string" ) value = Buffer . from ( value , "hex" ) ; this . _removeFieldByType ( FIELD_TYPE . SHORT_DESC ) ; this . _setFieldValue ( FIELD_TYPE . HASH_DESC , value ) ; } /**
     * Gets the 33-byte public key of the payee node. This is
     * used to explicitly describe the payee node instead of
     * relying on pub key recovery from the signature.
     *
     * Sets the 33-byte public key of the payee node. This is
     * used to set the public key explicitly instead of relying
     * on signature recovery. This field must match the pubkey
     * used to generate the signature.
     */ public get payeeNode ( ) : Buffer { return this . _getFieldValue ( FIELD_TYPE . PAYEE_NODE ) ; } public set payeeNode ( value ) { if ( typeof value === "string" ) value = Buffer . from ( value , "hex" ) ; this . _setFieldValue ( FIELD_TYPE . PAYEE_NODE , value ) ; } /**
     * Gets the min final route CLTV expiry. If none is provided,
     * the default is 9.
     *
     * Sets the min final route CLTV expiry used in the final route.
     */ public get minFinalCltvExpiry ( ) : number { return this . _getFieldValue ( FIELD_TYPE . MIN_FINAL_CLTV_EXPIRY , MIN_FINAL_CLTV_EXPIRY_DEFAULT ) ; } public set minFinalCltvExpiry ( value ) { this . _setFieldValue ( FIELD_TYPE . MIN_FINAL_CLTV_EXPIRY , value ) ; } /**
     * Gets a list of fall back addresses. An invoice can include
     * multiple fallback addresses to send to an on-chain address
     * in the event of failure.
     */ public get fallbackAddresses ( ) : FallbackAddress [ ] { return this . fields . filter ( p => p . type === FIELD_TYPE . FALLBACK_ADDRESS ) . map ( p => p . value ) as FallbackAddress [ ] ; } /**
     * Adds a fallback address to the invoice. An invoice can include
     * one or more fallback addresses to send to an on-chain address
     * in the event of failure. This field may not make sense for small
     * or time-sensitive payments.
     *
     * The address string will be parsed and the appropriate address
     * type is added to the field metadata. The address will be
     * converted into a buffer containing the string values of the
     * address.
     */ public addFallbackAddress ( addrStr ) { let version ; let address ; if ( addrStr . startsWith ( "1" ) || addrStr . startsWith ( "m" ) || addrStr . startsWith ( "n" ) ) { version = ADDRESS_VERSION . P2PKH ; address = bs58check . decode ( addrStr ) . slice ( 1 ) ; } else if ( addrStr . startsWith ( "3" ) || addrStr . startsWith ( "2" ) ) { version = ADDRESS_VERSION . P2SH ; address = bs58check . decode ( addrStr ) . slice ( 1 ) ; } else if ( addrStr . startsWith ( "bc1" ) || addrStr . startsWith ( "tb1" ) ) { const words = bech32 . decode ( addrStr ) . words ; version = words [ 0 ] ; address = bech32 . fromWords ( words . slice ( 1 ) ) ; } if ( Array . isArray ( address ) ) address = Buffer . from ( address ) ; this . fields . push ( { type : FIELD_TYPE . FALLBACK_ADDRESS , value : { version , address } } ) ; } /**
     * Gets the list of routes that are specified in the invoice.
     * Route information is necessary to route payments to private
     * nodes.
     */ public get routes ( ) : Route [ ] { return this . fields . filter ( p => p . type === FIELD_TYPE . ROUTE ) . map ( p => p . value ) ; } /**
     * Adds a collection of routes to the invoice. A route entry must
     * be provided by private nodes so that a public addressible node
     * can be found by the recipient.
     *
     * Multiple route fields can be added to an invoice in according
     * with BOLT 11 to give the routing options.
     */ public addRoute ( routes ) { for ( const route of routes ) { if ( typeof route . pubkey === "string" ) route . pubkey = Buffer . from ( route . pubkey , "hex" ) ; if ( typeof route . short_channel_id === "string" ) { route . short_channel_id = Buffer . from ( route . short_channel_id , "hex" ) ; } } this . fields . push ( { type : FIELD_TYPE . ROUTE , value : routes } ) ; } /**
     * Gets the value of thee first matching field that matches the field
     * type. If no result is found, the default value will be used.
     */ private _getFieldValue < T > ( type , def ? ) { const field = this . fields . find ( p => p . type === type ) ; return ( field ? field . value : def ) as T ; } /**
     * Sets the field value for the first matching field. If no
     * field exists it will insert a new field with the type and value
     * supplied.
     */ private _setFieldValue < T > ( type , value ) { const field = this . fields . find ( p => p . type === type ) ; if ( field ) field . value = value ; else this . fields . push ( { type , value } ) ; } /**
     * Removes the fields that match the supplied type.
     */ private _removeFieldByType ( type ) { this . fields = this . fields . filter ( p => p . type !== type ) ; } }