Compare commits

...

28 commits

Author SHA1 Message Date
Patrick Mulligan
99d83efd56 fix(nip05): add configurable limits for relays per user and identity listing
Some checks are pending
Docker Compose Actions Workflow / test (push) Waiting to run
Adds max_relays_per_user (default: 10) to prevent users from attaching
excessive relay URLs that inflate .well-known/nostr.json responses.
Enforced in both claimUsername and updateRelays.

Reduces the no-name listing limit from hardcoded 1000 to configurable
max_identities_listing (default: 100) for the /.well-known/nostr.json
endpoint when no ?name= parameter is provided.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 13:31:33 -04:00
Patrick Mulligan
17727d3e31 fix(nip05): add redirect prevention docs and zap field validation
Some checks are pending
Docker Compose Actions Workflow / test (push) Waiting to run
Gap #5: Document NIP-05 spec requirement that /.well-known/nostr.json
MUST NOT return HTTP redirects. The extension already complies (always
returns direct responses), but reverse proxy deployments need awareness.

Gap #7: Log a warning when getLnurlPayInfo() response is missing
allowsNostr or nostrPubkey fields required by NIP-57 for zap support.
This surfaces misconfiguration early instead of silently breaking zaps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 13:25:19 -04:00
Patrick Mulligan
53945d7dcc fix(nip05): allow hyphens and periods in usernames per NIP-05 spec
NIP-05 spec states local-part MUST only use characters a-z0-9-_.
The previous regex /^[a-z][a-z0-9_]*$/ rejected hyphens and periods.
Updated to /^[a-z][a-z0-9._-]*[a-z0-9]$/ and added support for the
root identifier "_" (_@domain) as described in the spec.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 13:25:06 -04:00
Patrick Mulligan
a7fb92e26d feat(nip05): add Lightning Address support for zaps
Adds /.well-known/lnurlp/:username endpoint that:
1. Looks up username in NIP-05 database
2. Gets LNURL-pay info from Lightning.Pub for that user
3. Returns standard LUD-16 response for wallet compatibility

This makes NIP-05 addresses (alice@domain) work seamlessly as
Lightning Addresses for receiving payments and NIP-57 zaps.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-01 13:24:45 -04:00
Patrick Mulligan
8f38622395 feat(extensions): add NIP-05 identity extension
Implements Nostr NIP-05 for human-readable identity verification:
- Username claiming and management (username@domain)
- /.well-known/nostr.json endpoint per spec
- Optional relay hints in JSON response
- Admin controls for identity management

RPC methods:
- nip05.claim - Claim a username
- nip05.release - Release your username
- nip05.updateRelays - Update relay hints
- nip05.getMyIdentity - Get your identity
- nip05.lookup - Look up by username
- nip05.lookupByPubkey - Look up by pubkey
- nip05.listIdentities - List all (admin)
- nip05.deactivate/reactivate - Admin controls

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-01 13:24:45 -04:00
Patrick Mulligan
5cc7f3998c fix(extensions): add HTTP route types and getHttpRoutes to Extension interface
Some checks are pending
Docker Compose Actions Workflow / test (push) Waiting to run
HttpRoute, HttpRequest, and HttpResponse types were used by extensions
(withdraw, nip05) but not defined in the shared types.ts. Adds them
here so extensions import from the shared module instead of defining
locally. Also adds getHttpRoutes() as an optional method on the
Extension interface for extensions that expose HTTP endpoints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 13:24:22 -04:00
Patrick Mulligan
c308d4be78 fix: use fresh balance in PayAppUserInvoice notification
Some checks failed
Docker Compose Actions Workflow / test (push) Has been cancelled
notifyAppUserPayment was sending the stale cached balance from the
entity loaded before PayInvoice decremented it. Update the entity's
balance_sats from the PayInvoice response so LiveUserOperation events
contain the correct post-payment balance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 18:45:27 -04:00
Patrick Mulligan
f1aa5d7139 chore: update Docker build and dependencies
- Add .dockerignore for runtime state files (sqlite, logs, secrets)
- Bump Node.js base image from 18 to 20
- Add @types/better-sqlite3 dev dependency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 18:45:27 -04:00
Patrick Mulligan
9981e2628e fix: correct nip44v1 secp256k1 getSharedSecret argument types
The @noble/curves secp256k1.getSharedSecret expects Uint8Array arguments,
not hex strings. Use hex.decode() to convert the private and public keys.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 18:45:27 -04:00
Patrick Mulligan
ece75df22d feat(extensions): add getLnurlPayInfo to ExtensionContext
Enables extensions to get LNURL-pay info for users by pubkey,
supporting Lightning Address (LUD-16) and zap (NIP-57) functionality.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 18:45:27 -04:00
Patrick Mulligan
c909cd660a docs(extensions): add comprehensive extension loader documentation
Covers architecture, API reference, lifecycle, database isolation,
RPC methods, HTTP routes, event handling, and complete examples.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 18:45:27 -04:00
Patrick Mulligan
5b88982fed feat(extensions): add extension loader infrastructure
Adds a modular extension system for Lightning.Pub that allows
third-party functionality to be added without modifying core code.

Features:
- ExtensionLoader: discovers and loads extensions from directory
- ExtensionContext: provides extensions with access to Lightning.Pub APIs
- ExtensionDatabase: isolated SQLite database per extension
- Lifecycle management: initialize, shutdown, health checks
- RPC method registration: extensions can add new RPC methods
- Event dispatching: routes payments and Nostr events to extensions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 18:45:27 -04:00
Patrick Mulligan
4d19b55c57 fix(nostr): close SimplePool after publishing to prevent connection leak
Each sendEvent() call created a new SimplePool() but never closed it,
causing relay WebSocket connections to accumulate indefinitely (~20/min).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 18:45:27 -04:00
Patrick Mulligan
071a27ad2d fix(handlers): await NostrSend calls throughout codebase
Update all NostrSend call sites to properly handle the async nature
of the function now that it returns Promise<void>.

Changes:
- handler.ts: Add async to sendResponse, await nostrSend calls
- debitManager.ts: Add logging for Kind 21002 response sending
- nostrMiddleware.ts: Update nostrSend signature
- tlvFilesStorageProcessor.ts: Update nostrSend signature
- webRTC/index.ts: Add async/await for nostrSend calls

This ensures Kind 21002 (ndebit) responses are properly sent to
wallet clients, fixing the "Debit request failed" issue in ShockWallet.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 18:45:27 -04:00
Patrick Mulligan
fee87e2741 fix(nostr): update NostrSend type to Promise<void> with error handling
The NostrSend type was incorrectly typed as returning void when it actually
returns Promise<void>. This caused async errors to be silently swallowed.

Changes:
- Update NostrSend type signature to return Promise<void>
- Make NostrSender._nostrSend default to async function
- Add .catch() error handling in NostrSender.Send() to log failures
- Add logging to track event publishing status

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 18:45:27 -04:00
Justin (shocknet)
ef53408485
Merge pull request #912 from shocknet/metrics-cache
Metrics cache
2026-03-26 14:17:09 -04:00
boufni95
1ad3166460
fix imports 2026-03-26 18:13:10 +00:00
boufni95
9499fdc923
metrics cache + update proto + queue fix 2026-03-26 17:59:43 +00:00
Justin (shocknet)
a163215860
Merge pull request #910 from shocknet/refund-swap-info
refund swap info
2026-03-12 15:20:00 -04:00
Justin (shocknet)
43efb63054
Merge pull request #911 from shocknet/fix-zaps
zaps fix
2026-03-12 15:19:27 -04:00
boufni95
423c4e9c73
undo ineffective change 2026-03-12 19:16:30 +00:00
boufni95
e7085e2ef3
fix zaps amt validation 2026-03-12 19:15:43 +00:00
boufni95
7b33669b51
zaps fix 2026-03-12 19:09:02 +00:00
boufni95
3c49b0fc07
refund swap info 2026-03-09 18:53:15 +00:00
Justin (shocknet)
9de5c1d982
Merge pull request #895 from shocknet/cleanup-logs
cleaup logs
2026-03-06 13:00:56 -05:00
Justin (shocknet)
b68129c316
Merge pull request #909 from shocknet/fix-swap-failure
fix swap crash
2026-03-06 11:03:55 -05:00
boufni95
0f3626869e
fix swap crash 2026-03-05 18:20:10 +00:00
boufni95
e2389b9b27
cleaup logs 2026-03-02 19:40:29 +00:00
58 changed files with 11234 additions and 4854 deletions

View file

@ -2,3 +2,21 @@
.github
build
node_modules
# Runtime state files (should not be baked into image)
*.sqlite
*.sqlite-journal
*.sqlite-wal
*.sqlite-shm
*.db
admin.connect
admin.enroll
admin.npub
app.nprofile
.jwt_secret
# Runtime data directories
metric_cache/
metric_events/
bundler_events/
logs/

View file

@ -1,4 +1,4 @@
FROM node:18
FROM node:20
WORKDIR /app

View file

@ -66,6 +66,7 @@ import { InvoiceSwaps1769529793283 } from './build/src/services/storage/migratio
import { InvoiceSwapsFixes1769805357459 } from './build/src/services/storage/migrations/1769805357459-invoice_swaps_fixes.js'
import { ApplicationUserTopicId1770038768784 } from './build/src/services/storage/migrations/1770038768784-application_user_topic_id.js'
import { SwapTimestamps1771347307798 } from './build/src/services/storage/migrations/1771347307798-swap_timestamps.js'
import { TxSwapTimestamps1771878683383 } from './build/src/services/storage/migrations/1771878683383-tx_swap_timestamps.js'
@ -80,7 +81,8 @@ export default new DataSource({
InvoiceCallbackUrls1752425992291, OldSomethingLeftover1753106599604, UserReceivingInvoiceIdx1753109184611, AppUserDevice1753285173175,
UserAccess1759426050669, AddBlindToUserOffer1760000000000, ApplicationAvatarUrl1761000001000, AdminSettings1761683639419, TxSwap1762890527098,
TxSwapAddress1764779178945, ClinkRequester1765497600000, TrackedProviderHeight1766504040000, SwapsServiceUrl1768413055036,
InvoiceSwaps1769529793283, InvoiceSwapsFixes1769805357459, ApplicationUserTopicId1770038768784, SwapTimestamps1771347307798
InvoiceSwaps1769529793283, InvoiceSwapsFixes1769805357459, ApplicationUserTopicId1770038768784, SwapTimestamps1771347307798,
TxSwapTimestamps1771878683383
],
@ -89,4 +91,4 @@ export default new DataSource({
TrackedProvider, InviteToken, DebitAccess, UserOffer, ManagementGrant, AppUserDevice, UserAccess, AdminSettings, TransactionSwap, InvoiceSwap],
// synchronize: true,
})
//npx typeorm migration:generate ./src/services/storage/migrations/tx_swap_timestamps -d ./datasource.js
//npx typeorm migration:generate ./src/services/storage/migrations/refund_swap_info -d ./datasource.js

2920
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -77,6 +77,7 @@
"zip-a-folder": "^3.1.9"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.13",
"@types/chai": "^4.3.4",
"@types/chai-string": "^1.4.5",
"@types/cors": "^2.8.17",

View file

@ -1431,6 +1431,9 @@ The nostr server will send back a message response, and inside the body there wi
- __failure_reason__: _string_ *this field is optional
- __operation_payment__: _[UserOperation](#UserOperation)_ *this field is optional
- __quote__: _[InvoiceSwapQuote](#InvoiceSwapQuote)_
- __refund_address__: _string_ *this field is optional
- __refund_at_unix__: _number_ *this field is optional
- __refund_tx_id__: _string_ *this field is optional
### InvoiceSwapQuote
- __address__: _string_

View file

@ -391,6 +391,9 @@ type InvoiceSwapOperation struct {
Failure_reason string `json:"failure_reason"`
Operation_payment *UserOperation `json:"operation_payment"`
Quote *InvoiceSwapQuote `json:"quote"`
Refund_address string `json:"refund_address"`
Refund_at_unix int64 `json:"refund_at_unix"`
Refund_tx_id string `json:"refund_tx_id"`
}
type InvoiceSwapQuote struct {
Address string `json:"address"`

View file

@ -2270,15 +2270,21 @@ export type InvoiceSwapOperation = {
failure_reason?: string
operation_payment?: UserOperation
quote: InvoiceSwapQuote
refund_address?: string
refund_at_unix?: number
refund_tx_id?: string
}
export type InvoiceSwapOperationOptionalField = 'completed_at_unix' | 'failure_reason' | 'operation_payment'
export const InvoiceSwapOperationOptionalFields: InvoiceSwapOperationOptionalField[] = ['completed_at_unix', 'failure_reason', 'operation_payment']
export type InvoiceSwapOperationOptionalField = 'completed_at_unix' | 'failure_reason' | 'operation_payment' | 'refund_address' | 'refund_at_unix' | 'refund_tx_id'
export const InvoiceSwapOperationOptionalFields: InvoiceSwapOperationOptionalField[] = ['completed_at_unix', 'failure_reason', 'operation_payment', 'refund_address', 'refund_at_unix', 'refund_tx_id']
export type InvoiceSwapOperationOptions = OptionsBaseMessage & {
checkOptionalsAreSet?: InvoiceSwapOperationOptionalField[]
completed_at_unix_CustomCheck?: (v?: number) => boolean
failure_reason_CustomCheck?: (v?: string) => boolean
operation_payment_Options?: UserOperationOptions
quote_Options?: InvoiceSwapQuoteOptions
refund_address_CustomCheck?: (v?: string) => boolean
refund_at_unix_CustomCheck?: (v?: number) => boolean
refund_tx_id_CustomCheck?: (v?: string) => boolean
}
export const InvoiceSwapOperationValidate = (o?: InvoiceSwapOperation, opts: InvoiceSwapOperationOptions = {}, path: string = 'InvoiceSwapOperation::root.'): Error | null => {
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
@ -2300,6 +2306,15 @@ export const InvoiceSwapOperationValidate = (o?: InvoiceSwapOperation, opts: Inv
if (quoteErr !== null) return quoteErr
if ((o.refund_address || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('refund_address')) && typeof o.refund_address !== 'string') return new Error(`${path}.refund_address: is not a string`)
if (opts.refund_address_CustomCheck && !opts.refund_address_CustomCheck(o.refund_address)) return new Error(`${path}.refund_address: custom check failed`)
if ((o.refund_at_unix || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('refund_at_unix')) && typeof o.refund_at_unix !== 'number') return new Error(`${path}.refund_at_unix: is not a number`)
if (opts.refund_at_unix_CustomCheck && !opts.refund_at_unix_CustomCheck(o.refund_at_unix)) return new Error(`${path}.refund_at_unix: custom check failed`)
if ((o.refund_tx_id || opts.allOptionalsAreSet || opts.checkOptionalsAreSet?.includes('refund_tx_id')) && typeof o.refund_tx_id !== 'string') return new Error(`${path}.refund_tx_id: is not a string`)
if (opts.refund_tx_id_CustomCheck && !opts.refund_tx_id_CustomCheck(o.refund_tx_id)) return new Error(`${path}.refund_tx_id: custom check failed`)
return null
}

View file

@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.8.1
// @generated by protobuf-ts 2.11.1
// @generated from protobuf file "chainnotifier.proto" (package "chainrpc", syntax proto3)
// tslint:disable
import type { RpcTransport } from "@protobuf-ts/runtime-rpc";
@ -31,7 +31,7 @@ export interface IChainNotifierClient {
* a notification is sent once the output script confirms in the given
* transaction.
*
* @generated from protobuf rpc: RegisterConfirmationsNtfn(chainrpc.ConfRequest) returns (stream chainrpc.ConfEvent);
* @generated from protobuf rpc: RegisterConfirmationsNtfn
*/
registerConfirmationsNtfn(input: ConfRequest, options?: RpcOptions): ServerStreamingCall<ConfRequest, ConfEvent>;
/**
@ -43,7 +43,7 @@ export interface IChainNotifierClient {
* A client can specify whether the spend request should be for a particular
* outpoint or for an output script by specifying a zero outpoint.
*
* @generated from protobuf rpc: RegisterSpendNtfn(chainrpc.SpendRequest) returns (stream chainrpc.SpendEvent);
* @generated from protobuf rpc: RegisterSpendNtfn
*/
registerSpendNtfn(input: SpendRequest, options?: RpcOptions): ServerStreamingCall<SpendRequest, SpendEvent>;
/**
@ -58,7 +58,7 @@ export interface IChainNotifierClient {
* point. This allows clients to be idempotent by ensuring that they do not
* missing processing a single block within the chain.
*
* @generated from protobuf rpc: RegisterBlockEpochNtfn(chainrpc.BlockEpoch) returns (stream chainrpc.BlockEpoch);
* @generated from protobuf rpc: RegisterBlockEpochNtfn
*/
registerBlockEpochNtfn(input: BlockEpoch, options?: RpcOptions): ServerStreamingCall<BlockEpoch, BlockEpoch>;
}
@ -86,7 +86,7 @@ export class ChainNotifierClient implements IChainNotifierClient, ServiceInfo {
* a notification is sent once the output script confirms in the given
* transaction.
*
* @generated from protobuf rpc: RegisterConfirmationsNtfn(chainrpc.ConfRequest) returns (stream chainrpc.ConfEvent);
* @generated from protobuf rpc: RegisterConfirmationsNtfn
*/
registerConfirmationsNtfn(input: ConfRequest, options?: RpcOptions): ServerStreamingCall<ConfRequest, ConfEvent> {
const method = this.methods[0], opt = this._transport.mergeOptions(options);
@ -101,7 +101,7 @@ export class ChainNotifierClient implements IChainNotifierClient, ServiceInfo {
* A client can specify whether the spend request should be for a particular
* outpoint or for an output script by specifying a zero outpoint.
*
* @generated from protobuf rpc: RegisterSpendNtfn(chainrpc.SpendRequest) returns (stream chainrpc.SpendEvent);
* @generated from protobuf rpc: RegisterSpendNtfn
*/
registerSpendNtfn(input: SpendRequest, options?: RpcOptions): ServerStreamingCall<SpendRequest, SpendEvent> {
const method = this.methods[1], opt = this._transport.mergeOptions(options);
@ -119,7 +119,7 @@ export class ChainNotifierClient implements IChainNotifierClient, ServiceInfo {
* point. This allows clients to be idempotent by ensuring that they do not
* missing processing a single block within the chain.
*
* @generated from protobuf rpc: RegisterBlockEpochNtfn(chainrpc.BlockEpoch) returns (stream chainrpc.BlockEpoch);
* @generated from protobuf rpc: RegisterBlockEpochNtfn
*/
registerBlockEpochNtfn(input: BlockEpoch, options?: RpcOptions): ServerStreamingCall<BlockEpoch, BlockEpoch> {
const method = this.methods[2], opt = this._transport.mergeOptions(options);

View file

@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.8.1
// @generated by protobuf-ts 2.11.1
// @generated from protobuf file "chainnotifier.proto" (package "chainrpc", syntax proto3)
// tslint:disable
import { ServiceType } from "@protobuf-ts/runtime-rpc";
@ -10,7 +10,6 @@ import type { IBinaryReader } from "@protobuf-ts/runtime";
import { UnknownFieldHandler } from "@protobuf-ts/runtime";
import type { PartialMessage } from "@protobuf-ts/runtime";
import { reflectionMergePartial } from "@protobuf-ts/runtime";
import { MESSAGE_TYPE } from "@protobuf-ts/runtime";
import { MessageType } from "@protobuf-ts/runtime";
/**
* @generated from protobuf message chainrpc.ConfRequest
@ -22,7 +21,7 @@ export interface ConfRequest {
* for. If set to a hash of all zeros, then the confirmation notification will
* be requested for the script instead.
*
* @generated from protobuf field: bytes txid = 1;
* @generated from protobuf field: bytes txid = 1
*/
txid: Uint8Array;
/**
@ -32,7 +31,7 @@ export interface ConfRequest {
* hash of all zeros, then a confirmation notification will be requested for
* this script instead.
*
* @generated from protobuf field: bytes script = 2;
* @generated from protobuf field: bytes script = 2
*/
script: Uint8Array;
/**
@ -40,7 +39,7 @@ export interface ConfRequest {
* The number of desired confirmations the transaction/output script should
* reach before dispatching a confirmation notification.
*
* @generated from protobuf field: uint32 num_confs = 3;
* @generated from protobuf field: uint32 num_confs = 3
*/
numConfs: number;
/**
@ -49,7 +48,7 @@ export interface ConfRequest {
* could have been included in a block. This should in most cases be set to the
* broadcast height of the transaction/output script.
*
* @generated from protobuf field: uint32 height_hint = 4;
* @generated from protobuf field: uint32 height_hint = 4
*/
heightHint: number;
/**
@ -57,7 +56,7 @@ export interface ConfRequest {
* If true, then the block that mines the specified txid/script will be
* included in eventual the notification event.
*
* @generated from protobuf field: bool include_block = 5;
* @generated from protobuf field: bool include_block = 5
*/
includeBlock: boolean;
}
@ -68,26 +67,26 @@ export interface ConfDetails {
/**
* The raw bytes of the confirmed transaction.
*
* @generated from protobuf field: bytes raw_tx = 1;
* @generated from protobuf field: bytes raw_tx = 1
*/
rawTx: Uint8Array;
/**
* The hash of the block in which the confirmed transaction was included in.
*
* @generated from protobuf field: bytes block_hash = 2;
* @generated from protobuf field: bytes block_hash = 2
*/
blockHash: Uint8Array;
/**
* The height of the block in which the confirmed transaction was included
* in.
*
* @generated from protobuf field: uint32 block_height = 3;
* @generated from protobuf field: uint32 block_height = 3
*/
blockHeight: number;
/**
* The index of the confirmed transaction within the block.
*
* @generated from protobuf field: uint32 tx_index = 4;
* @generated from protobuf field: uint32 tx_index = 4
*/
txIndex: number;
/**
@ -95,7 +94,7 @@ export interface ConfDetails {
* The raw bytes of the block that mined the transaction. Only included if
* include_block was set in the request.
*
* @generated from protobuf field: bytes raw_block = 5;
* @generated from protobuf field: bytes raw_block = 5
*/
rawBlock: Uint8Array;
}
@ -120,7 +119,7 @@ export interface ConfEvent {
* An event that includes the confirmation details of the request
* (txid/ouput script).
*
* @generated from protobuf field: chainrpc.ConfDetails conf = 1;
* @generated from protobuf field: chainrpc.ConfDetails conf = 1
*/
conf: ConfDetails;
} | {
@ -130,7 +129,7 @@ export interface ConfEvent {
* An event send when the transaction of the request is reorged out of the
* chain.
*
* @generated from protobuf field: chainrpc.Reorg reorg = 2;
* @generated from protobuf field: chainrpc.Reorg reorg = 2
*/
reorg: Reorg;
} | {
@ -144,13 +143,13 @@ export interface Outpoint {
/**
* The hash of the transaction.
*
* @generated from protobuf field: bytes hash = 1;
* @generated from protobuf field: bytes hash = 1
*/
hash: Uint8Array;
/**
* The index of the output within the transaction.
*
* @generated from protobuf field: uint32 index = 2;
* @generated from protobuf field: uint32 index = 2
*/
index: number;
}
@ -168,7 +167,7 @@ export interface SpendRequest {
* So an outpoint must _always_ be specified when registering a spend
* notification for a Taproot output.
*
* @generated from protobuf field: chainrpc.Outpoint outpoint = 1;
* @generated from protobuf field: chainrpc.Outpoint outpoint = 1
*/
outpoint?: Outpoint;
/**
@ -177,7 +176,7 @@ export interface SpendRequest {
* to match block filters. If the outpoint is set to a zero outpoint, then a
* spend notification will be requested for this script instead.
*
* @generated from protobuf field: bytes script = 2;
* @generated from protobuf field: bytes script = 2
*/
script: Uint8Array;
/**
@ -186,7 +185,7 @@ export interface SpendRequest {
* have been spent. This should in most cases be set to the broadcast height of
* the outpoint/output script.
*
* @generated from protobuf field: uint32 height_hint = 3;
* @generated from protobuf field: uint32 height_hint = 3
*/
heightHint: number;
}
@ -197,31 +196,31 @@ export interface SpendDetails {
/**
* The outpoint was that spent.
*
* @generated from protobuf field: chainrpc.Outpoint spending_outpoint = 1;
* @generated from protobuf field: chainrpc.Outpoint spending_outpoint = 1
*/
spendingOutpoint?: Outpoint;
/**
* The raw bytes of the spending transaction.
*
* @generated from protobuf field: bytes raw_spending_tx = 2;
* @generated from protobuf field: bytes raw_spending_tx = 2
*/
rawSpendingTx: Uint8Array;
/**
* The hash of the spending transaction.
*
* @generated from protobuf field: bytes spending_tx_hash = 3;
* @generated from protobuf field: bytes spending_tx_hash = 3
*/
spendingTxHash: Uint8Array;
/**
* The input of the spending transaction that fulfilled the spend request.
*
* @generated from protobuf field: uint32 spending_input_index = 4;
* @generated from protobuf field: uint32 spending_input_index = 4
*/
spendingInputIndex: number;
/**
* The height at which the spending transaction was included in a block.
*
* @generated from protobuf field: uint32 spending_height = 5;
* @generated from protobuf field: uint32 spending_height = 5
*/
spendingHeight: number;
}
@ -239,7 +238,7 @@ export interface SpendEvent {
* An event that includes the details of the spending transaction of the
* request (outpoint/output script).
*
* @generated from protobuf field: chainrpc.SpendDetails spend = 1;
* @generated from protobuf field: chainrpc.SpendDetails spend = 1
*/
spend: SpendDetails;
} | {
@ -249,7 +248,7 @@ export interface SpendEvent {
* An event sent when the spending transaction of the request was
* reorged out of the chain.
*
* @generated from protobuf field: chainrpc.Reorg reorg = 2;
* @generated from protobuf field: chainrpc.Reorg reorg = 2
*/
reorg: Reorg;
} | {
@ -263,13 +262,13 @@ export interface BlockEpoch {
/**
* The hash of the block.
*
* @generated from protobuf field: bytes hash = 1;
* @generated from protobuf field: bytes hash = 1
*/
hash: Uint8Array;
/**
* The height of the block.
*
* @generated from protobuf field: uint32 height = 2;
* @generated from protobuf field: uint32 height = 2
*/
height: number;
}
@ -285,8 +284,12 @@ class ConfRequest$Type extends MessageType<ConfRequest> {
]);
}
create(value?: PartialMessage<ConfRequest>): ConfRequest {
const message = { txid: new Uint8Array(0), script: new Uint8Array(0), numConfs: 0, heightHint: 0, includeBlock: false };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.txid = new Uint8Array(0);
message.script = new Uint8Array(0);
message.numConfs = 0;
message.heightHint = 0;
message.includeBlock = false;
if (value !== undefined)
reflectionMergePartial<ConfRequest>(this, message, value);
return message;
@ -360,8 +363,12 @@ class ConfDetails$Type extends MessageType<ConfDetails> {
]);
}
create(value?: PartialMessage<ConfDetails>): ConfDetails {
const message = { rawTx: new Uint8Array(0), blockHash: new Uint8Array(0), blockHeight: 0, txIndex: 0, rawBlock: new Uint8Array(0) };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.rawTx = new Uint8Array(0);
message.blockHash = new Uint8Array(0);
message.blockHeight = 0;
message.txIndex = 0;
message.rawBlock = new Uint8Array(0);
if (value !== undefined)
reflectionMergePartial<ConfDetails>(this, message, value);
return message;
@ -429,14 +436,26 @@ class Reorg$Type extends MessageType<Reorg> {
super("chainrpc.Reorg", []);
}
create(value?: PartialMessage<Reorg>): Reorg {
const message = {};
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
if (value !== undefined)
reflectionMergePartial<Reorg>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Reorg): Reorg {
return target ?? this.create();
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: Reorg, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
let u = options.writeUnknownFields;
@ -458,8 +477,8 @@ class ConfEvent$Type extends MessageType<ConfEvent> {
]);
}
create(value?: PartialMessage<ConfEvent>): ConfEvent {
const message = { event: { oneofKind: undefined } };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.event = { oneofKind: undefined };
if (value !== undefined)
reflectionMergePartial<ConfEvent>(this, message, value);
return message;
@ -518,8 +537,9 @@ class Outpoint$Type extends MessageType<Outpoint> {
]);
}
create(value?: PartialMessage<Outpoint>): Outpoint {
const message = { hash: new Uint8Array(0), index: 0 };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.hash = new Uint8Array(0);
message.index = 0;
if (value !== undefined)
reflectionMergePartial<Outpoint>(this, message, value);
return message;
@ -573,8 +593,9 @@ class SpendRequest$Type extends MessageType<SpendRequest> {
]);
}
create(value?: PartialMessage<SpendRequest>): SpendRequest {
const message = { script: new Uint8Array(0), heightHint: 0 };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.script = new Uint8Array(0);
message.heightHint = 0;
if (value !== undefined)
reflectionMergePartial<SpendRequest>(this, message, value);
return message;
@ -636,8 +657,11 @@ class SpendDetails$Type extends MessageType<SpendDetails> {
]);
}
create(value?: PartialMessage<SpendDetails>): SpendDetails {
const message = { rawSpendingTx: new Uint8Array(0), spendingTxHash: new Uint8Array(0), spendingInputIndex: 0, spendingHeight: 0 };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.rawSpendingTx = new Uint8Array(0);
message.spendingTxHash = new Uint8Array(0);
message.spendingInputIndex = 0;
message.spendingHeight = 0;
if (value !== undefined)
reflectionMergePartial<SpendDetails>(this, message, value);
return message;
@ -708,8 +732,8 @@ class SpendEvent$Type extends MessageType<SpendEvent> {
]);
}
create(value?: PartialMessage<SpendEvent>): SpendEvent {
const message = { event: { oneofKind: undefined } };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.event = { oneofKind: undefined };
if (value !== undefined)
reflectionMergePartial<SpendEvent>(this, message, value);
return message;
@ -768,8 +792,9 @@ class BlockEpoch$Type extends MessageType<BlockEpoch> {
]);
}
create(value?: PartialMessage<BlockEpoch>): BlockEpoch {
const message = { hash: new Uint8Array(0), height: 0 };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.hash = new Uint8Array(0);
message.height = 0;
if (value !== undefined)
reflectionMergePartial<BlockEpoch>(this, message, value);
return message;

View file

@ -1,9 +1,12 @@
// @generated by protobuf-ts 2.8.1
// @generated by protobuf-ts 2.11.1
// @generated from protobuf file "invoices.proto" (package "invoicesrpc", syntax proto3)
// tslint:disable
import type { RpcTransport } from "@protobuf-ts/runtime-rpc";
import type { ServiceInfo } from "@protobuf-ts/runtime-rpc";
import { Invoices } from "./invoices.js";
import type { HtlcModifyRequest } from "./invoices.js";
import type { HtlcModifyResponse } from "./invoices.js";
import type { DuplexStreamingCall } from "@protobuf-ts/runtime-rpc";
import type { LookupInvoiceMsg } from "./invoices.js";
import type { SettleInvoiceResp } from "./invoices.js";
import type { SettleInvoiceMsg } from "./invoices.js";
@ -17,6 +20,23 @@ import type { Invoice } from "./lightning.js";
import type { SubscribeSingleInvoiceRequest } from "./invoices.js";
import type { ServerStreamingCall } from "@protobuf-ts/runtime-rpc";
import type { RpcOptions } from "@protobuf-ts/runtime-rpc";
//
// Comments in this file will be directly parsed into the API
// Documentation as descriptions of the associated method, message, or field.
// These descriptions should go right above the definition of the object, and
// can be in either block or // comment format.
//
// An RPC method can be matched to an lncli command by placing a line in the
// beginning of the description in exactly the following format:
// lncli: `methodname`
//
// Failure to specify the exact name of the command will cause documentation
// generation to fail.
//
// More information on how exactly the gRPC documentation is generated from
// this proto file can be found here:
// https://github.com/lightninglabs/lightning-api
/**
* Invoices is a service that can be used to create, accept, settle and cancel
* invoices.
@ -30,43 +50,70 @@ export interface IInvoicesClient {
* to notify the client of state transitions of the specified invoice.
* Initially the current invoice state is always sent out.
*
* @generated from protobuf rpc: SubscribeSingleInvoice(invoicesrpc.SubscribeSingleInvoiceRequest) returns (stream lnrpc.Invoice);
* @generated from protobuf rpc: SubscribeSingleInvoice
*/
subscribeSingleInvoice(input: SubscribeSingleInvoiceRequest, options?: RpcOptions): ServerStreamingCall<SubscribeSingleInvoiceRequest, Invoice>;
/**
*
* lncli: `cancelinvoice`
* CancelInvoice cancels a currently open invoice. If the invoice is already
* canceled, this call will succeed. If the invoice is already settled, it will
* fail.
*
* @generated from protobuf rpc: CancelInvoice(invoicesrpc.CancelInvoiceMsg) returns (invoicesrpc.CancelInvoiceResp);
* @generated from protobuf rpc: CancelInvoice
*/
cancelInvoice(input: CancelInvoiceMsg, options?: RpcOptions): UnaryCall<CancelInvoiceMsg, CancelInvoiceResp>;
/**
*
* lncli: `addholdinvoice`
* AddHoldInvoice creates a hold invoice. It ties the invoice to the hash
* supplied in the request.
*
* @generated from protobuf rpc: AddHoldInvoice(invoicesrpc.AddHoldInvoiceRequest) returns (invoicesrpc.AddHoldInvoiceResp);
* @generated from protobuf rpc: AddHoldInvoice
*/
addHoldInvoice(input: AddHoldInvoiceRequest, options?: RpcOptions): UnaryCall<AddHoldInvoiceRequest, AddHoldInvoiceResp>;
/**
*
* lncli: `settleinvoice`
* SettleInvoice settles an accepted invoice. If the invoice is already
* settled, this call will succeed.
*
* @generated from protobuf rpc: SettleInvoice(invoicesrpc.SettleInvoiceMsg) returns (invoicesrpc.SettleInvoiceResp);
* @generated from protobuf rpc: SettleInvoice
*/
settleInvoice(input: SettleInvoiceMsg, options?: RpcOptions): UnaryCall<SettleInvoiceMsg, SettleInvoiceResp>;
/**
*
* LookupInvoiceV2 attempts to look up at invoice. An invoice can be refrenced
* LookupInvoiceV2 attempts to look up at invoice. An invoice can be referenced
* using either its payment hash, payment address, or set ID.
*
* @generated from protobuf rpc: LookupInvoiceV2(invoicesrpc.LookupInvoiceMsg) returns (lnrpc.Invoice);
* @generated from protobuf rpc: LookupInvoiceV2
*/
lookupInvoiceV2(input: LookupInvoiceMsg, options?: RpcOptions): UnaryCall<LookupInvoiceMsg, Invoice>;
/**
*
* HtlcModifier is a bidirectional streaming RPC that allows a client to
* intercept and modify the HTLCs that attempt to settle the given invoice. The
* server will send HTLCs of invoices to the client and the client can modify
* some aspects of the HTLC in order to pass the invoice acceptance tests.
*
* @generated from protobuf rpc: HtlcModifier
*/
htlcModifier(options?: RpcOptions): DuplexStreamingCall<HtlcModifyResponse, HtlcModifyRequest>;
}
//
// Comments in this file will be directly parsed into the API
// Documentation as descriptions of the associated method, message, or field.
// These descriptions should go right above the definition of the object, and
// can be in either block or // comment format.
//
// An RPC method can be matched to an lncli command by placing a line in the
// beginning of the description in exactly the following format:
// lncli: `methodname`
//
// Failure to specify the exact name of the command will cause documentation
// generation to fail.
//
// More information on how exactly the gRPC documentation is generated from
// this proto file can be found here:
// https://github.com/lightninglabs/lightning-api
/**
* Invoices is a service that can be used to create, accept, settle and cancel
* invoices.
@ -85,41 +132,41 @@ export class InvoicesClient implements IInvoicesClient, ServiceInfo {
* to notify the client of state transitions of the specified invoice.
* Initially the current invoice state is always sent out.
*
* @generated from protobuf rpc: SubscribeSingleInvoice(invoicesrpc.SubscribeSingleInvoiceRequest) returns (stream lnrpc.Invoice);
* @generated from protobuf rpc: SubscribeSingleInvoice
*/
subscribeSingleInvoice(input: SubscribeSingleInvoiceRequest, options?: RpcOptions): ServerStreamingCall<SubscribeSingleInvoiceRequest, Invoice> {
const method = this.methods[0], opt = this._transport.mergeOptions(options);
return stackIntercept<SubscribeSingleInvoiceRequest, Invoice>("serverStreaming", this._transport, method, opt, input);
}
/**
*
* lncli: `cancelinvoice`
* CancelInvoice cancels a currently open invoice. If the invoice is already
* canceled, this call will succeed. If the invoice is already settled, it will
* fail.
*
* @generated from protobuf rpc: CancelInvoice(invoicesrpc.CancelInvoiceMsg) returns (invoicesrpc.CancelInvoiceResp);
* @generated from protobuf rpc: CancelInvoice
*/
cancelInvoice(input: CancelInvoiceMsg, options?: RpcOptions): UnaryCall<CancelInvoiceMsg, CancelInvoiceResp> {
const method = this.methods[1], opt = this._transport.mergeOptions(options);
return stackIntercept<CancelInvoiceMsg, CancelInvoiceResp>("unary", this._transport, method, opt, input);
}
/**
*
* lncli: `addholdinvoice`
* AddHoldInvoice creates a hold invoice. It ties the invoice to the hash
* supplied in the request.
*
* @generated from protobuf rpc: AddHoldInvoice(invoicesrpc.AddHoldInvoiceRequest) returns (invoicesrpc.AddHoldInvoiceResp);
* @generated from protobuf rpc: AddHoldInvoice
*/
addHoldInvoice(input: AddHoldInvoiceRequest, options?: RpcOptions): UnaryCall<AddHoldInvoiceRequest, AddHoldInvoiceResp> {
const method = this.methods[2], opt = this._transport.mergeOptions(options);
return stackIntercept<AddHoldInvoiceRequest, AddHoldInvoiceResp>("unary", this._transport, method, opt, input);
}
/**
*
* lncli: `settleinvoice`
* SettleInvoice settles an accepted invoice. If the invoice is already
* settled, this call will succeed.
*
* @generated from protobuf rpc: SettleInvoice(invoicesrpc.SettleInvoiceMsg) returns (invoicesrpc.SettleInvoiceResp);
* @generated from protobuf rpc: SettleInvoice
*/
settleInvoice(input: SettleInvoiceMsg, options?: RpcOptions): UnaryCall<SettleInvoiceMsg, SettleInvoiceResp> {
const method = this.methods[3], opt = this._transport.mergeOptions(options);
@ -127,13 +174,26 @@ export class InvoicesClient implements IInvoicesClient, ServiceInfo {
}
/**
*
* LookupInvoiceV2 attempts to look up at invoice. An invoice can be refrenced
* LookupInvoiceV2 attempts to look up at invoice. An invoice can be referenced
* using either its payment hash, payment address, or set ID.
*
* @generated from protobuf rpc: LookupInvoiceV2(invoicesrpc.LookupInvoiceMsg) returns (lnrpc.Invoice);
* @generated from protobuf rpc: LookupInvoiceV2
*/
lookupInvoiceV2(input: LookupInvoiceMsg, options?: RpcOptions): UnaryCall<LookupInvoiceMsg, Invoice> {
const method = this.methods[4], opt = this._transport.mergeOptions(options);
return stackIntercept<LookupInvoiceMsg, Invoice>("unary", this._transport, method, opt, input);
}
/**
*
* HtlcModifier is a bidirectional streaming RPC that allows a client to
* intercept and modify the HTLCs that attempt to settle the given invoice. The
* server will send HTLCs of invoices to the client and the client can modify
* some aspects of the HTLC in order to pass the invoice acceptance tests.
*
* @generated from protobuf rpc: HtlcModifier
*/
htlcModifier(options?: RpcOptions): DuplexStreamingCall<HtlcModifyResponse, HtlcModifyRequest> {
const method = this.methods[5], opt = this._transport.mergeOptions(options);
return stackIntercept<HtlcModifyResponse, HtlcModifyRequest>("duplex", this._transport, method, opt);
}
}

View file

@ -1,7 +1,6 @@
// @generated by protobuf-ts 2.8.1
// @generated by protobuf-ts 2.11.1
// @generated from protobuf file "invoices.proto" (package "invoicesrpc", syntax proto3)
// tslint:disable
import { Invoice } from "./lightning.js";
import { ServiceType } from "@protobuf-ts/runtime-rpc";
import type { BinaryWriteOptions } from "@protobuf-ts/runtime";
import type { IBinaryWriter } from "@protobuf-ts/runtime";
@ -11,8 +10,8 @@ import type { IBinaryReader } from "@protobuf-ts/runtime";
import { UnknownFieldHandler } from "@protobuf-ts/runtime";
import type { PartialMessage } from "@protobuf-ts/runtime";
import { reflectionMergePartial } from "@protobuf-ts/runtime";
import { MESSAGE_TYPE } from "@protobuf-ts/runtime";
import { MessageType } from "@protobuf-ts/runtime";
import { Invoice } from "./lightning.js";
import { RouteHint } from "./lightning.js";
/**
* @generated from protobuf message invoicesrpc.CancelInvoiceMsg
@ -22,7 +21,7 @@ export interface CancelInvoiceMsg {
* Hash corresponding to the (hold) invoice to cancel. When using
* REST, this field must be encoded as base64.
*
* @generated from protobuf field: bytes payment_hash = 1;
* @generated from protobuf field: bytes payment_hash = 1
*/
paymentHash: Uint8Array;
}
@ -42,13 +41,13 @@ export interface AddHoldInvoiceRequest {
* field of the encoded payment request if the description_hash field is not
* being used.
*
* @generated from protobuf field: string memo = 1;
* @generated from protobuf field: string memo = 1
*/
memo: string;
/**
* The hash of the preimage
*
* @generated from protobuf field: bytes hash = 2;
* @generated from protobuf field: bytes hash = 2
*/
hash: Uint8Array;
/**
@ -57,7 +56,7 @@ export interface AddHoldInvoiceRequest {
*
* The fields value and value_msat are mutually exclusive.
*
* @generated from protobuf field: int64 value = 3;
* @generated from protobuf field: int64 value = 3
*/
value: bigint;
/**
@ -66,7 +65,7 @@ export interface AddHoldInvoiceRequest {
*
* The fields value and value_msat are mutually exclusive.
*
* @generated from protobuf field: int64 value_msat = 10;
* @generated from protobuf field: int64 value_msat = 10
*/
valueMsat: bigint;
/**
@ -75,25 +74,25 @@ export interface AddHoldInvoiceRequest {
* payment (memo) is too long to naturally fit within the description field
* of an encoded payment request.
*
* @generated from protobuf field: bytes description_hash = 4;
* @generated from protobuf field: bytes description_hash = 4
*/
descriptionHash: Uint8Array;
/**
* Payment request expiry time in seconds. Default is 3600 (1 hour).
* Payment request expiry time in seconds. Default is 86400 (24 hours).
*
* @generated from protobuf field: int64 expiry = 5;
* @generated from protobuf field: int64 expiry = 5
*/
expiry: bigint;
/**
* Fallback on-chain address.
*
* @generated from protobuf field: string fallback_addr = 6;
* @generated from protobuf field: string fallback_addr = 6
*/
fallbackAddr: string;
/**
* Delta to use for the time-lock of the CLTV extended to the final hop.
*
* @generated from protobuf field: uint64 cltv_expiry = 7;
* @generated from protobuf field: uint64 cltv_expiry = 7
*/
cltvExpiry: bigint;
/**
@ -101,13 +100,13 @@ export interface AddHoldInvoiceRequest {
* Route hints that can each be individually used to assist in reaching the
* invoice's destination.
*
* @generated from protobuf field: repeated lnrpc.RouteHint route_hints = 8;
* @generated from protobuf field: repeated lnrpc.RouteHint route_hints = 8
*/
routeHints: RouteHint[];
/**
* Whether this invoice should include routing hints for private channels.
*
* @generated from protobuf field: bool private = 9;
* @generated from protobuf field: bool private = 9
*/
private: boolean;
}
@ -121,7 +120,7 @@ export interface AddHoldInvoiceResp {
* details of the invoice, the sender has all the data necessary to send a
* payment to the recipient.
*
* @generated from protobuf field: string payment_request = 1;
* @generated from protobuf field: string payment_request = 1
*/
paymentRequest: string;
/**
@ -131,16 +130,17 @@ export interface AddHoldInvoiceResp {
* SubscribeInvoices call can use this to instantly get notified of all added
* invoices with an add_index greater than this one.
*
* @generated from protobuf field: uint64 add_index = 2;
* @generated from protobuf field: uint64 add_index = 2
*/
addIndex: bigint;
/**
*
* The payment address of the generated invoice. This value should be used
* in all payments for this invoice as we require it for end to end
* The payment address of the generated invoice. This is also called
* the payment secret in specifications (e.g. BOLT 11). This value should
* be used in all payments for this invoice as we require it for end to end
* security.
*
* @generated from protobuf field: bytes payment_addr = 3;
* @generated from protobuf field: bytes payment_addr = 3
*/
paymentAddr: Uint8Array;
}
@ -152,7 +152,7 @@ export interface SettleInvoiceMsg {
* Externally discovered pre-image that should be used to settle the hold
* invoice.
*
* @generated from protobuf field: bytes preimage = 1;
* @generated from protobuf field: bytes preimage = 1
*/
preimage: Uint8Array;
}
@ -169,7 +169,7 @@ export interface SubscribeSingleInvoiceRequest {
* Hash corresponding to the (hold) invoice to subscribe to. When using
* REST, this field must be encoded as base64url.
*
* @generated from protobuf field: bytes r_hash = 2;
* @generated from protobuf field: bytes r_hash = 2
*/
rHash: Uint8Array;
}
@ -185,29 +185,122 @@ export interface LookupInvoiceMsg {
/**
* When using REST, this field must be encoded as base64.
*
* @generated from protobuf field: bytes payment_hash = 1;
* @generated from protobuf field: bytes payment_hash = 1
*/
paymentHash: Uint8Array;
} | {
oneofKind: "paymentAddr";
/**
* @generated from protobuf field: bytes payment_addr = 2;
* @generated from protobuf field: bytes payment_addr = 2
*/
paymentAddr: Uint8Array;
} | {
oneofKind: "setId";
/**
* @generated from protobuf field: bytes set_id = 3;
* @generated from protobuf field: bytes set_id = 3
*/
setId: Uint8Array;
} | {
oneofKind: undefined;
};
/**
* @generated from protobuf field: invoicesrpc.LookupModifier lookup_modifier = 4;
* @generated from protobuf field: invoicesrpc.LookupModifier lookup_modifier = 4
*/
lookupModifier: LookupModifier;
}
/**
* CircuitKey is a unique identifier for an HTLC.
*
* @generated from protobuf message invoicesrpc.CircuitKey
*/
export interface CircuitKey {
/**
* The id of the channel that the is part of this circuit.
*
* @generated from protobuf field: uint64 chan_id = 1
*/
chanId: bigint;
/**
* The index of the incoming htlc in the incoming channel.
*
* @generated from protobuf field: uint64 htlc_id = 2
*/
htlcId: bigint;
}
/**
* @generated from protobuf message invoicesrpc.HtlcModifyRequest
*/
export interface HtlcModifyRequest {
/**
* The invoice the intercepted HTLC is attempting to settle. The HTLCs in
* the invoice are only HTLCs that have already been accepted or settled,
* not including the current intercepted HTLC.
*
* @generated from protobuf field: lnrpc.Invoice invoice = 1
*/
invoice?: Invoice;
/**
* The unique identifier of the HTLC of this intercepted HTLC.
*
* @generated from protobuf field: invoicesrpc.CircuitKey exit_htlc_circuit_key = 2
*/
exitHtlcCircuitKey?: CircuitKey;
/**
* The amount in milli-satoshi that the exit HTLC is attempting to pay.
*
* @generated from protobuf field: uint64 exit_htlc_amt = 3
*/
exitHtlcAmt: bigint;
/**
* The absolute expiry height of the exit HTLC.
*
* @generated from protobuf field: uint32 exit_htlc_expiry = 4
*/
exitHtlcExpiry: number;
/**
* The current block height.
*
* @generated from protobuf field: uint32 current_height = 5
*/
currentHeight: number;
/**
* The wire message custom records of the exit HTLC.
*
* @generated from protobuf field: map<uint64, bytes> exit_htlc_wire_custom_records = 6
*/
exitHtlcWireCustomRecords: {
[key: string]: Uint8Array;
};
}
/**
* @generated from protobuf message invoicesrpc.HtlcModifyResponse
*/
export interface HtlcModifyResponse {
/**
* The circuit key of the HTLC that the client wants to modify.
*
* @generated from protobuf field: invoicesrpc.CircuitKey circuit_key = 1
*/
circuitKey?: CircuitKey;
/**
* The modified amount in milli-satoshi that the exit HTLC is paying. This
* value can be different from the actual on-chain HTLC amount, in case the
* HTLC carries other valuable items, as can be the case with custom channel
* types.
*
* @generated from protobuf field: optional uint64 amt_paid = 2
*/
amtPaid?: bigint;
/**
* This flag indicates whether the HTLCs associated with the invoices should
* be cancelled. The interceptor client may set this field if some
* unexpected behavior is encountered. Setting this will ignore the amt_paid
* field.
*
* @generated from protobuf field: bool cancel_set = 3
*/
cancelSet: boolean;
}
/**
* @generated from protobuf enum invoicesrpc.LookupModifier
*/
@ -245,8 +338,8 @@ class CancelInvoiceMsg$Type extends MessageType<CancelInvoiceMsg> {
]);
}
create(value?: PartialMessage<CancelInvoiceMsg>): CancelInvoiceMsg {
const message = { paymentHash: new Uint8Array(0) };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.paymentHash = new Uint8Array(0);
if (value !== undefined)
reflectionMergePartial<CancelInvoiceMsg>(this, message, value);
return message;
@ -290,14 +383,26 @@ class CancelInvoiceResp$Type extends MessageType<CancelInvoiceResp> {
super("invoicesrpc.CancelInvoiceResp", []);
}
create(value?: PartialMessage<CancelInvoiceResp>): CancelInvoiceResp {
const message = {};
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
if (value !== undefined)
reflectionMergePartial<CancelInvoiceResp>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: CancelInvoiceResp): CancelInvoiceResp {
return target ?? this.create();
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: CancelInvoiceResp, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
let u = options.writeUnknownFields;
@ -322,13 +427,22 @@ class AddHoldInvoiceRequest$Type extends MessageType<AddHoldInvoiceRequest> {
{ no: 5, name: "expiry", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
{ no: 6, name: "fallback_addr", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 7, name: "cltv_expiry", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
{ no: 8, name: "route_hints", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => RouteHint },
{ no: 8, name: "route_hints", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => RouteHint },
{ no: 9, name: "private", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
]);
}
create(value?: PartialMessage<AddHoldInvoiceRequest>): AddHoldInvoiceRequest {
const message = { memo: "", hash: new Uint8Array(0), value: 0n, valueMsat: 0n, descriptionHash: new Uint8Array(0), expiry: 0n, fallbackAddr: "", cltvExpiry: 0n, routeHints: [], private: false };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.memo = "";
message.hash = new Uint8Array(0);
message.value = 0n;
message.valueMsat = 0n;
message.descriptionHash = new Uint8Array(0);
message.expiry = 0n;
message.fallbackAddr = "";
message.cltvExpiry = 0n;
message.routeHints = [];
message.private = false;
if (value !== undefined)
reflectionMergePartial<AddHoldInvoiceRequest>(this, message, value);
return message;
@ -389,9 +503,6 @@ class AddHoldInvoiceRequest$Type extends MessageType<AddHoldInvoiceRequest> {
/* int64 value = 3; */
if (message.value !== 0n)
writer.tag(3, WireType.Varint).int64(message.value);
/* int64 value_msat = 10; */
if (message.valueMsat !== 0n)
writer.tag(10, WireType.Varint).int64(message.valueMsat);
/* bytes description_hash = 4; */
if (message.descriptionHash.length)
writer.tag(4, WireType.LengthDelimited).bytes(message.descriptionHash);
@ -410,6 +521,9 @@ class AddHoldInvoiceRequest$Type extends MessageType<AddHoldInvoiceRequest> {
/* bool private = 9; */
if (message.private !== false)
writer.tag(9, WireType.Varint).bool(message.private);
/* int64 value_msat = 10; */
if (message.valueMsat !== 0n)
writer.tag(10, WireType.Varint).int64(message.valueMsat);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@ -430,8 +544,10 @@ class AddHoldInvoiceResp$Type extends MessageType<AddHoldInvoiceResp> {
]);
}
create(value?: PartialMessage<AddHoldInvoiceResp>): AddHoldInvoiceResp {
const message = { paymentRequest: "", addIndex: 0n, paymentAddr: new Uint8Array(0) };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.paymentRequest = "";
message.addIndex = 0n;
message.paymentAddr = new Uint8Array(0);
if (value !== undefined)
reflectionMergePartial<AddHoldInvoiceResp>(this, message, value);
return message;
@ -489,8 +605,8 @@ class SettleInvoiceMsg$Type extends MessageType<SettleInvoiceMsg> {
]);
}
create(value?: PartialMessage<SettleInvoiceMsg>): SettleInvoiceMsg {
const message = { preimage: new Uint8Array(0) };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.preimage = new Uint8Array(0);
if (value !== undefined)
reflectionMergePartial<SettleInvoiceMsg>(this, message, value);
return message;
@ -534,14 +650,26 @@ class SettleInvoiceResp$Type extends MessageType<SettleInvoiceResp> {
super("invoicesrpc.SettleInvoiceResp", []);
}
create(value?: PartialMessage<SettleInvoiceResp>): SettleInvoiceResp {
const message = {};
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
if (value !== undefined)
reflectionMergePartial<SettleInvoiceResp>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SettleInvoiceResp): SettleInvoiceResp {
return target ?? this.create();
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: SettleInvoiceResp, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
let u = options.writeUnknownFields;
@ -562,8 +690,8 @@ class SubscribeSingleInvoiceRequest$Type extends MessageType<SubscribeSingleInvo
]);
}
create(value?: PartialMessage<SubscribeSingleInvoiceRequest>): SubscribeSingleInvoiceRequest {
const message = { rHash: new Uint8Array(0) };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.rHash = new Uint8Array(0);
if (value !== undefined)
reflectionMergePartial<SubscribeSingleInvoiceRequest>(this, message, value);
return message;
@ -612,8 +740,9 @@ class LookupInvoiceMsg$Type extends MessageType<LookupInvoiceMsg> {
]);
}
create(value?: PartialMessage<LookupInvoiceMsg>): LookupInvoiceMsg {
const message = { invoiceRef: { oneofKind: undefined }, lookupModifier: 0 };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.invoiceRef = { oneofKind: undefined };
message.lookupModifier = 0;
if (value !== undefined)
reflectionMergePartial<LookupInvoiceMsg>(this, message, value);
return message;
@ -678,6 +807,223 @@ class LookupInvoiceMsg$Type extends MessageType<LookupInvoiceMsg> {
* @generated MessageType for protobuf message invoicesrpc.LookupInvoiceMsg
*/
export const LookupInvoiceMsg = new LookupInvoiceMsg$Type();
// @generated message type with reflection information, may provide speed optimized methods
class CircuitKey$Type extends MessageType<CircuitKey> {
constructor() {
super("invoicesrpc.CircuitKey", [
{ no: 1, name: "chan_id", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
{ no: 2, name: "htlc_id", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ }
]);
}
create(value?: PartialMessage<CircuitKey>): CircuitKey {
const message = globalThis.Object.create((this.messagePrototype!));
message.chanId = 0n;
message.htlcId = 0n;
if (value !== undefined)
reflectionMergePartial<CircuitKey>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: CircuitKey): CircuitKey {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* uint64 chan_id */ 1:
message.chanId = reader.uint64().toBigInt();
break;
case /* uint64 htlc_id */ 2:
message.htlcId = reader.uint64().toBigInt();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: CircuitKey, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* uint64 chan_id = 1; */
if (message.chanId !== 0n)
writer.tag(1, WireType.Varint).uint64(message.chanId);
/* uint64 htlc_id = 2; */
if (message.htlcId !== 0n)
writer.tag(2, WireType.Varint).uint64(message.htlcId);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message invoicesrpc.CircuitKey
*/
export const CircuitKey = new CircuitKey$Type();
// @generated message type with reflection information, may provide speed optimized methods
class HtlcModifyRequest$Type extends MessageType<HtlcModifyRequest> {
constructor() {
super("invoicesrpc.HtlcModifyRequest", [
{ no: 1, name: "invoice", kind: "message", T: () => Invoice },
{ no: 2, name: "exit_htlc_circuit_key", kind: "message", T: () => CircuitKey },
{ no: 3, name: "exit_htlc_amt", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
{ no: 4, name: "exit_htlc_expiry", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
{ no: 5, name: "current_height", kind: "scalar", T: 13 /*ScalarType.UINT32*/ },
{ no: 6, name: "exit_htlc_wire_custom_records", kind: "map", K: 4 /*ScalarType.UINT64*/, V: { kind: "scalar", T: 12 /*ScalarType.BYTES*/ } }
]);
}
create(value?: PartialMessage<HtlcModifyRequest>): HtlcModifyRequest {
const message = globalThis.Object.create((this.messagePrototype!));
message.exitHtlcAmt = 0n;
message.exitHtlcExpiry = 0;
message.currentHeight = 0;
message.exitHtlcWireCustomRecords = {};
if (value !== undefined)
reflectionMergePartial<HtlcModifyRequest>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: HtlcModifyRequest): HtlcModifyRequest {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* lnrpc.Invoice invoice */ 1:
message.invoice = Invoice.internalBinaryRead(reader, reader.uint32(), options, message.invoice);
break;
case /* invoicesrpc.CircuitKey exit_htlc_circuit_key */ 2:
message.exitHtlcCircuitKey = CircuitKey.internalBinaryRead(reader, reader.uint32(), options, message.exitHtlcCircuitKey);
break;
case /* uint64 exit_htlc_amt */ 3:
message.exitHtlcAmt = reader.uint64().toBigInt();
break;
case /* uint32 exit_htlc_expiry */ 4:
message.exitHtlcExpiry = reader.uint32();
break;
case /* uint32 current_height */ 5:
message.currentHeight = reader.uint32();
break;
case /* map<uint64, bytes> exit_htlc_wire_custom_records */ 6:
this.binaryReadMap6(message.exitHtlcWireCustomRecords, reader, options);
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
private binaryReadMap6(map: HtlcModifyRequest["exitHtlcWireCustomRecords"], reader: IBinaryReader, options: BinaryReadOptions): void {
let len = reader.uint32(), end = reader.pos + len, key: keyof HtlcModifyRequest["exitHtlcWireCustomRecords"] | undefined, val: HtlcModifyRequest["exitHtlcWireCustomRecords"][any] | undefined;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case 1:
key = reader.uint64().toString();
break;
case 2:
val = reader.bytes();
break;
default: throw new globalThis.Error("unknown map entry field for invoicesrpc.HtlcModifyRequest.exit_htlc_wire_custom_records");
}
}
map[key ?? "0"] = val ?? new Uint8Array(0);
}
internalBinaryWrite(message: HtlcModifyRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* lnrpc.Invoice invoice = 1; */
if (message.invoice)
Invoice.internalBinaryWrite(message.invoice, writer.tag(1, WireType.LengthDelimited).fork(), options).join();
/* invoicesrpc.CircuitKey exit_htlc_circuit_key = 2; */
if (message.exitHtlcCircuitKey)
CircuitKey.internalBinaryWrite(message.exitHtlcCircuitKey, writer.tag(2, WireType.LengthDelimited).fork(), options).join();
/* uint64 exit_htlc_amt = 3; */
if (message.exitHtlcAmt !== 0n)
writer.tag(3, WireType.Varint).uint64(message.exitHtlcAmt);
/* uint32 exit_htlc_expiry = 4; */
if (message.exitHtlcExpiry !== 0)
writer.tag(4, WireType.Varint).uint32(message.exitHtlcExpiry);
/* uint32 current_height = 5; */
if (message.currentHeight !== 0)
writer.tag(5, WireType.Varint).uint32(message.currentHeight);
/* map<uint64, bytes> exit_htlc_wire_custom_records = 6; */
for (let k of globalThis.Object.keys(message.exitHtlcWireCustomRecords))
writer.tag(6, WireType.LengthDelimited).fork().tag(1, WireType.Varint).uint64(k).tag(2, WireType.LengthDelimited).bytes(message.exitHtlcWireCustomRecords[k]).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message invoicesrpc.HtlcModifyRequest
*/
export const HtlcModifyRequest = new HtlcModifyRequest$Type();
// @generated message type with reflection information, may provide speed optimized methods
class HtlcModifyResponse$Type extends MessageType<HtlcModifyResponse> {
constructor() {
super("invoicesrpc.HtlcModifyResponse", [
{ no: 1, name: "circuit_key", kind: "message", T: () => CircuitKey },
{ no: 2, name: "amt_paid", kind: "scalar", opt: true, T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
{ no: 3, name: "cancel_set", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
]);
}
create(value?: PartialMessage<HtlcModifyResponse>): HtlcModifyResponse {
const message = globalThis.Object.create((this.messagePrototype!));
message.cancelSet = false;
if (value !== undefined)
reflectionMergePartial<HtlcModifyResponse>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: HtlcModifyResponse): HtlcModifyResponse {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* invoicesrpc.CircuitKey circuit_key */ 1:
message.circuitKey = CircuitKey.internalBinaryRead(reader, reader.uint32(), options, message.circuitKey);
break;
case /* optional uint64 amt_paid */ 2:
message.amtPaid = reader.uint64().toBigInt();
break;
case /* bool cancel_set */ 3:
message.cancelSet = reader.bool();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: HtlcModifyResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* invoicesrpc.CircuitKey circuit_key = 1; */
if (message.circuitKey)
CircuitKey.internalBinaryWrite(message.circuitKey, writer.tag(1, WireType.LengthDelimited).fork(), options).join();
/* optional uint64 amt_paid = 2; */
if (message.amtPaid !== undefined)
writer.tag(2, WireType.Varint).uint64(message.amtPaid);
/* bool cancel_set = 3; */
if (message.cancelSet !== false)
writer.tag(3, WireType.Varint).bool(message.cancelSet);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message invoicesrpc.HtlcModifyResponse
*/
export const HtlcModifyResponse = new HtlcModifyResponse$Type();
/**
* @generated ServiceType for protobuf service invoicesrpc.Invoices
*/
@ -686,5 +1032,6 @@ export const Invoices = new ServiceType("invoicesrpc.Invoices", [
{ name: "CancelInvoice", options: {}, I: CancelInvoiceMsg, O: CancelInvoiceResp },
{ name: "AddHoldInvoice", options: {}, I: AddHoldInvoiceRequest, O: AddHoldInvoiceResp },
{ name: "SettleInvoice", options: {}, I: SettleInvoiceMsg, O: SettleInvoiceResp },
{ name: "LookupInvoiceV2", options: {}, I: LookupInvoiceMsg, O: Invoice }
{ name: "LookupInvoiceV2", options: {}, I: LookupInvoiceMsg, O: Invoice },
{ name: "HtlcModifier", serverStreaming: true, clientStreaming: true, options: {}, I: HtlcModifyResponse, O: HtlcModifyRequest }
]);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,13 @@
// @generated by protobuf-ts 2.8.1
// @generated by protobuf-ts 2.11.1
// @generated from protobuf file "router.proto" (package "routerrpc", syntax proto3)
// tslint:disable
import type { RpcTransport } from "@protobuf-ts/runtime-rpc";
import type { ServiceInfo } from "@protobuf-ts/runtime-rpc";
import { Router } from "./router.js";
import type { DeleteAliasesResponse } from "./router.js";
import type { DeleteAliasesRequest } from "./router.js";
import type { AddAliasesResponse } from "./router.js";
import type { AddAliasesRequest } from "./router.js";
import type { UpdateChanStatusResponse } from "./router.js";
import type { UpdateChanStatusRequest } from "./router.js";
import type { ForwardHtlcInterceptRequest } from "./router.js";
@ -39,6 +43,23 @@ import type { Payment } from "./lightning.js";
import type { SendPaymentRequest } from "./router.js";
import type { ServerStreamingCall } from "@protobuf-ts/runtime-rpc";
import type { RpcOptions } from "@protobuf-ts/runtime-rpc";
//
// Comments in this file will be directly parsed into the API
// Documentation as descriptions of the associated method, message, or field.
// These descriptions should go right above the definition of the object, and
// can be in either block or // comment format.
//
// An RPC method can be matched to an lncli command by placing a line in the
// beginning of the description in exactly the following format:
// lncli: `methodname`
//
// Failure to specify the exact name of the command will cause documentation
// generation to fail.
//
// More information on how exactly the gRPC documentation is generated from
// this proto file can be found here:
// https://github.com/lightninglabs/lightning-api
/**
* Router is a service that offers advanced interaction with the router
* subsystem of the daemon.
@ -50,17 +71,20 @@ export interface IRouterClient {
*
* SendPaymentV2 attempts to route a payment described by the passed
* PaymentRequest to the final destination. The call returns a stream of
* payment updates.
* payment updates. When using this RPC, make sure to set a fee limit, as the
* default routing fee limit is 0 sats. Without a non-zero fee limit only
* routes without fees will be attempted which often fails with
* FAILURE_REASON_NO_ROUTE.
*
* @generated from protobuf rpc: SendPaymentV2(routerrpc.SendPaymentRequest) returns (stream lnrpc.Payment);
* @generated from protobuf rpc: SendPaymentV2
*/
sendPaymentV2(input: SendPaymentRequest, options?: RpcOptions): ServerStreamingCall<SendPaymentRequest, Payment>;
/**
*
* lncli: `trackpayment`
* TrackPaymentV2 returns an update stream for the payment identified by the
* payment hash.
*
* @generated from protobuf rpc: TrackPaymentV2(routerrpc.TrackPaymentRequest) returns (stream lnrpc.Payment);
* @generated from protobuf rpc: TrackPaymentV2
*/
trackPaymentV2(input: TrackPaymentRequest, options?: RpcOptions): ServerStreamingCall<TrackPaymentRequest, Payment>;
/**
@ -72,7 +96,7 @@ export interface IRouterClient {
* payment attempt make sure to subscribe to this method before initiating any
* payments.
*
* @generated from protobuf rpc: TrackPayments(routerrpc.TrackPaymentsRequest) returns (stream lnrpc.Payment);
* @generated from protobuf rpc: TrackPayments
*/
trackPayments(input: TrackPaymentsRequest, options?: RpcOptions): ServerStreamingCall<TrackPaymentsRequest, Payment>;
/**
@ -80,7 +104,7 @@ export interface IRouterClient {
* EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it
* may cost to send an HTLC to the target end destination.
*
* @generated from protobuf rpc: EstimateRouteFee(routerrpc.RouteFeeRequest) returns (routerrpc.RouteFeeResponse);
* @generated from protobuf rpc: EstimateRouteFee
*/
estimateRouteFee(input: RouteFeeRequest, options?: RpcOptions): UnaryCall<RouteFeeRequest, RouteFeeResponse>;
/**
@ -92,7 +116,7 @@ export interface IRouterClient {
* SendToRouteV2 in that it doesn't return the full HTLC information.
*
* @deprecated
* @generated from protobuf rpc: SendToRoute(routerrpc.SendToRouteRequest) returns (routerrpc.SendToRouteResponse);
* @generated from protobuf rpc: SendToRoute
*/
sendToRoute(input: SendToRouteRequest, options?: RpcOptions): UnaryCall<SendToRouteRequest, SendToRouteResponse>;
/**
@ -102,65 +126,71 @@ export interface IRouterClient {
* route manually. This can be used for things like rebalancing, and atomic
* swaps.
*
* @generated from protobuf rpc: SendToRouteV2(routerrpc.SendToRouteRequest) returns (lnrpc.HTLCAttempt);
* @generated from protobuf rpc: SendToRouteV2
*/
sendToRouteV2(input: SendToRouteRequest, options?: RpcOptions): UnaryCall<SendToRouteRequest, HTLCAttempt>;
/**
*
* lncli: `resetmc`
* ResetMissionControl clears all mission control state and starts with a clean
* slate.
*
* @generated from protobuf rpc: ResetMissionControl(routerrpc.ResetMissionControlRequest) returns (routerrpc.ResetMissionControlResponse);
* @generated from protobuf rpc: ResetMissionControl
*/
resetMissionControl(input: ResetMissionControlRequest, options?: RpcOptions): UnaryCall<ResetMissionControlRequest, ResetMissionControlResponse>;
/**
*
* lncli: `querymc`
* QueryMissionControl exposes the internal mission control state to callers.
* It is a development feature.
*
* @generated from protobuf rpc: QueryMissionControl(routerrpc.QueryMissionControlRequest) returns (routerrpc.QueryMissionControlResponse);
* @generated from protobuf rpc: QueryMissionControl
*/
queryMissionControl(input: QueryMissionControlRequest, options?: RpcOptions): UnaryCall<QueryMissionControlRequest, QueryMissionControlResponse>;
/**
*
* lncli: `importmc`
* XImportMissionControl is an experimental API that imports the state provided
* to the internal mission control's state, using all results which are more
* recent than our existing values. These values will only be imported
* in-memory, and will not be persisted across restarts.
*
* @generated from protobuf rpc: XImportMissionControl(routerrpc.XImportMissionControlRequest) returns (routerrpc.XImportMissionControlResponse);
* @generated from protobuf rpc: XImportMissionControl
*/
xImportMissionControl(input: XImportMissionControlRequest, options?: RpcOptions): UnaryCall<XImportMissionControlRequest, XImportMissionControlResponse>;
/**
*
* lncli: `getmccfg`
* GetMissionControlConfig returns mission control's current config.
*
* @generated from protobuf rpc: GetMissionControlConfig(routerrpc.GetMissionControlConfigRequest) returns (routerrpc.GetMissionControlConfigResponse);
* @generated from protobuf rpc: GetMissionControlConfig
*/
getMissionControlConfig(input: GetMissionControlConfigRequest, options?: RpcOptions): UnaryCall<GetMissionControlConfigRequest, GetMissionControlConfigResponse>;
/**
*
* lncli: `setmccfg`
* SetMissionControlConfig will set mission control's config, if the config
* provided is valid.
*
* @generated from protobuf rpc: SetMissionControlConfig(routerrpc.SetMissionControlConfigRequest) returns (routerrpc.SetMissionControlConfigResponse);
* @generated from protobuf rpc: SetMissionControlConfig
*/
setMissionControlConfig(input: SetMissionControlConfigRequest, options?: RpcOptions): UnaryCall<SetMissionControlConfigRequest, SetMissionControlConfigResponse>;
/**
* lncli: `queryprob`
* Deprecated. QueryProbability returns the current success probability
* estimate for a given node pair and amount. The call returns a zero success
* probability if no channel is available or if the amount violates min/max
* HTLC constraints.
*
* QueryProbability returns the current success probability estimate for a
* given node pair and amount.
*
* @generated from protobuf rpc: QueryProbability(routerrpc.QueryProbabilityRequest) returns (routerrpc.QueryProbabilityResponse);
* @generated from protobuf rpc: QueryProbability
*/
queryProbability(input: QueryProbabilityRequest, options?: RpcOptions): UnaryCall<QueryProbabilityRequest, QueryProbabilityResponse>;
/**
*
* lncli: `buildroute`
* BuildRoute builds a fully specified route based on a list of hop public
* keys. It retrieves the relevant channel policies from the graph in order to
* calculate the correct fees and time locks.
* Note that LND will use its default final_cltv_delta if no value is supplied.
* Make sure to add the correct final_cltv_delta depending on the invoice
* restriction. Moreover the caller has to make sure to provide the
* payment_addr if the route is paying an invoice which signaled it.
*
* @generated from protobuf rpc: BuildRoute(routerrpc.BuildRouteRequest) returns (routerrpc.BuildRouteResponse);
* @generated from protobuf rpc: BuildRoute
*/
buildRoute(input: BuildRouteRequest, options?: RpcOptions): UnaryCall<BuildRouteRequest, BuildRouteResponse>;
/**
@ -168,7 +198,7 @@ export interface IRouterClient {
* SubscribeHtlcEvents creates a uni-directional stream from the server to
* the client which delivers a stream of htlc events.
*
* @generated from protobuf rpc: SubscribeHtlcEvents(routerrpc.SubscribeHtlcEventsRequest) returns (stream routerrpc.HtlcEvent);
* @generated from protobuf rpc: SubscribeHtlcEvents
*/
subscribeHtlcEvents(input: SubscribeHtlcEventsRequest, options?: RpcOptions): ServerStreamingCall<SubscribeHtlcEventsRequest, HtlcEvent>;
/**
@ -178,7 +208,7 @@ export interface IRouterClient {
* returns a stream of payment status updates.
*
* @deprecated
* @generated from protobuf rpc: SendPayment(routerrpc.SendPaymentRequest) returns (stream routerrpc.PaymentStatus);
* @generated from protobuf rpc: SendPayment
*/
sendPayment(input: SendPaymentRequest, options?: RpcOptions): ServerStreamingCall<SendPaymentRequest, PaymentStatus>;
/**
@ -187,7 +217,7 @@ export interface IRouterClient {
* the payment identified by the payment hash.
*
* @deprecated
* @generated from protobuf rpc: TrackPayment(routerrpc.TrackPaymentRequest) returns (stream routerrpc.PaymentStatus);
* @generated from protobuf rpc: TrackPayment
*/
trackPayment(input: TrackPaymentRequest, options?: RpcOptions): ServerStreamingCall<TrackPaymentRequest, PaymentStatus>;
/**
@ -198,20 +228,59 @@ export interface IRouterClient {
* In case of interception, the htlc can be either settled, cancelled or
* resumed later by using the ResolveHoldForward endpoint.
*
* @generated from protobuf rpc: HtlcInterceptor(stream routerrpc.ForwardHtlcInterceptResponse) returns (stream routerrpc.ForwardHtlcInterceptRequest);
* @generated from protobuf rpc: HtlcInterceptor
*/
htlcInterceptor(options?: RpcOptions): DuplexStreamingCall<ForwardHtlcInterceptResponse, ForwardHtlcInterceptRequest>;
/**
*
* lncli: `updatechanstatus`
* UpdateChanStatus attempts to manually set the state of a channel
* (enabled, disabled, or auto). A manual "disable" request will cause the
* channel to stay disabled until a subsequent manual request of either
* "enable" or "auto".
*
* @generated from protobuf rpc: UpdateChanStatus(routerrpc.UpdateChanStatusRequest) returns (routerrpc.UpdateChanStatusResponse);
* @generated from protobuf rpc: UpdateChanStatus
*/
updateChanStatus(input: UpdateChanStatusRequest, options?: RpcOptions): UnaryCall<UpdateChanStatusRequest, UpdateChanStatusResponse>;
/**
*
* XAddLocalChanAliases is an experimental API that creates a set of new
* channel SCID alias mappings. The final total set of aliases in the manager
* after the add operation is returned. This is only a locally stored alias,
* and will not be communicated to the channel peer via any message. Therefore,
* routing over such an alias will only work if the peer also calls this same
* RPC on their end. If an alias already exists, an error is returned
*
* @generated from protobuf rpc: XAddLocalChanAliases
*/
xAddLocalChanAliases(input: AddAliasesRequest, options?: RpcOptions): UnaryCall<AddAliasesRequest, AddAliasesResponse>;
/**
*
* XDeleteLocalChanAliases is an experimental API that deletes a set of alias
* mappings. The final total set of aliases in the manager after the delete
* operation is returned. The deletion will not be communicated to the channel
* peer via any message.
*
* @generated from protobuf rpc: XDeleteLocalChanAliases
*/
xDeleteLocalChanAliases(input: DeleteAliasesRequest, options?: RpcOptions): UnaryCall<DeleteAliasesRequest, DeleteAliasesResponse>;
}
//
// Comments in this file will be directly parsed into the API
// Documentation as descriptions of the associated method, message, or field.
// These descriptions should go right above the definition of the object, and
// can be in either block or // comment format.
//
// An RPC method can be matched to an lncli command by placing a line in the
// beginning of the description in exactly the following format:
// lncli: `methodname`
//
// Failure to specify the exact name of the command will cause documentation
// generation to fail.
//
// More information on how exactly the gRPC documentation is generated from
// this proto file can be found here:
// https://github.com/lightninglabs/lightning-api
/**
* Router is a service that offers advanced interaction with the router
* subsystem of the daemon.
@ -228,20 +297,23 @@ export class RouterClient implements IRouterClient, ServiceInfo {
*
* SendPaymentV2 attempts to route a payment described by the passed
* PaymentRequest to the final destination. The call returns a stream of
* payment updates.
* payment updates. When using this RPC, make sure to set a fee limit, as the
* default routing fee limit is 0 sats. Without a non-zero fee limit only
* routes without fees will be attempted which often fails with
* FAILURE_REASON_NO_ROUTE.
*
* @generated from protobuf rpc: SendPaymentV2(routerrpc.SendPaymentRequest) returns (stream lnrpc.Payment);
* @generated from protobuf rpc: SendPaymentV2
*/
sendPaymentV2(input: SendPaymentRequest, options?: RpcOptions): ServerStreamingCall<SendPaymentRequest, Payment> {
const method = this.methods[0], opt = this._transport.mergeOptions(options);
return stackIntercept<SendPaymentRequest, Payment>("serverStreaming", this._transport, method, opt, input);
}
/**
*
* lncli: `trackpayment`
* TrackPaymentV2 returns an update stream for the payment identified by the
* payment hash.
*
* @generated from protobuf rpc: TrackPaymentV2(routerrpc.TrackPaymentRequest) returns (stream lnrpc.Payment);
* @generated from protobuf rpc: TrackPaymentV2
*/
trackPaymentV2(input: TrackPaymentRequest, options?: RpcOptions): ServerStreamingCall<TrackPaymentRequest, Payment> {
const method = this.methods[1], opt = this._transport.mergeOptions(options);
@ -256,7 +328,7 @@ export class RouterClient implements IRouterClient, ServiceInfo {
* payment attempt make sure to subscribe to this method before initiating any
* payments.
*
* @generated from protobuf rpc: TrackPayments(routerrpc.TrackPaymentsRequest) returns (stream lnrpc.Payment);
* @generated from protobuf rpc: TrackPayments
*/
trackPayments(input: TrackPaymentsRequest, options?: RpcOptions): ServerStreamingCall<TrackPaymentsRequest, Payment> {
const method = this.methods[2], opt = this._transport.mergeOptions(options);
@ -267,7 +339,7 @@ export class RouterClient implements IRouterClient, ServiceInfo {
* EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it
* may cost to send an HTLC to the target end destination.
*
* @generated from protobuf rpc: EstimateRouteFee(routerrpc.RouteFeeRequest) returns (routerrpc.RouteFeeResponse);
* @generated from protobuf rpc: EstimateRouteFee
*/
estimateRouteFee(input: RouteFeeRequest, options?: RpcOptions): UnaryCall<RouteFeeRequest, RouteFeeResponse> {
const method = this.methods[3], opt = this._transport.mergeOptions(options);
@ -282,7 +354,7 @@ export class RouterClient implements IRouterClient, ServiceInfo {
* SendToRouteV2 in that it doesn't return the full HTLC information.
*
* @deprecated
* @generated from protobuf rpc: SendToRoute(routerrpc.SendToRouteRequest) returns (routerrpc.SendToRouteResponse);
* @generated from protobuf rpc: SendToRoute
*/
sendToRoute(input: SendToRouteRequest, options?: RpcOptions): UnaryCall<SendToRouteRequest, SendToRouteResponse> {
const method = this.methods[4], opt = this._transport.mergeOptions(options);
@ -295,86 +367,92 @@ export class RouterClient implements IRouterClient, ServiceInfo {
* route manually. This can be used for things like rebalancing, and atomic
* swaps.
*
* @generated from protobuf rpc: SendToRouteV2(routerrpc.SendToRouteRequest) returns (lnrpc.HTLCAttempt);
* @generated from protobuf rpc: SendToRouteV2
*/
sendToRouteV2(input: SendToRouteRequest, options?: RpcOptions): UnaryCall<SendToRouteRequest, HTLCAttempt> {
const method = this.methods[5], opt = this._transport.mergeOptions(options);
return stackIntercept<SendToRouteRequest, HTLCAttempt>("unary", this._transport, method, opt, input);
}
/**
*
* lncli: `resetmc`
* ResetMissionControl clears all mission control state and starts with a clean
* slate.
*
* @generated from protobuf rpc: ResetMissionControl(routerrpc.ResetMissionControlRequest) returns (routerrpc.ResetMissionControlResponse);
* @generated from protobuf rpc: ResetMissionControl
*/
resetMissionControl(input: ResetMissionControlRequest, options?: RpcOptions): UnaryCall<ResetMissionControlRequest, ResetMissionControlResponse> {
const method = this.methods[6], opt = this._transport.mergeOptions(options);
return stackIntercept<ResetMissionControlRequest, ResetMissionControlResponse>("unary", this._transport, method, opt, input);
}
/**
*
* lncli: `querymc`
* QueryMissionControl exposes the internal mission control state to callers.
* It is a development feature.
*
* @generated from protobuf rpc: QueryMissionControl(routerrpc.QueryMissionControlRequest) returns (routerrpc.QueryMissionControlResponse);
* @generated from protobuf rpc: QueryMissionControl
*/
queryMissionControl(input: QueryMissionControlRequest, options?: RpcOptions): UnaryCall<QueryMissionControlRequest, QueryMissionControlResponse> {
const method = this.methods[7], opt = this._transport.mergeOptions(options);
return stackIntercept<QueryMissionControlRequest, QueryMissionControlResponse>("unary", this._transport, method, opt, input);
}
/**
*
* lncli: `importmc`
* XImportMissionControl is an experimental API that imports the state provided
* to the internal mission control's state, using all results which are more
* recent than our existing values. These values will only be imported
* in-memory, and will not be persisted across restarts.
*
* @generated from protobuf rpc: XImportMissionControl(routerrpc.XImportMissionControlRequest) returns (routerrpc.XImportMissionControlResponse);
* @generated from protobuf rpc: XImportMissionControl
*/
xImportMissionControl(input: XImportMissionControlRequest, options?: RpcOptions): UnaryCall<XImportMissionControlRequest, XImportMissionControlResponse> {
const method = this.methods[8], opt = this._transport.mergeOptions(options);
return stackIntercept<XImportMissionControlRequest, XImportMissionControlResponse>("unary", this._transport, method, opt, input);
}
/**
*
* lncli: `getmccfg`
* GetMissionControlConfig returns mission control's current config.
*
* @generated from protobuf rpc: GetMissionControlConfig(routerrpc.GetMissionControlConfigRequest) returns (routerrpc.GetMissionControlConfigResponse);
* @generated from protobuf rpc: GetMissionControlConfig
*/
getMissionControlConfig(input: GetMissionControlConfigRequest, options?: RpcOptions): UnaryCall<GetMissionControlConfigRequest, GetMissionControlConfigResponse> {
const method = this.methods[9], opt = this._transport.mergeOptions(options);
return stackIntercept<GetMissionControlConfigRequest, GetMissionControlConfigResponse>("unary", this._transport, method, opt, input);
}
/**
*
* lncli: `setmccfg`
* SetMissionControlConfig will set mission control's config, if the config
* provided is valid.
*
* @generated from protobuf rpc: SetMissionControlConfig(routerrpc.SetMissionControlConfigRequest) returns (routerrpc.SetMissionControlConfigResponse);
* @generated from protobuf rpc: SetMissionControlConfig
*/
setMissionControlConfig(input: SetMissionControlConfigRequest, options?: RpcOptions): UnaryCall<SetMissionControlConfigRequest, SetMissionControlConfigResponse> {
const method = this.methods[10], opt = this._transport.mergeOptions(options);
return stackIntercept<SetMissionControlConfigRequest, SetMissionControlConfigResponse>("unary", this._transport, method, opt, input);
}
/**
* lncli: `queryprob`
* Deprecated. QueryProbability returns the current success probability
* estimate for a given node pair and amount. The call returns a zero success
* probability if no channel is available or if the amount violates min/max
* HTLC constraints.
*
* QueryProbability returns the current success probability estimate for a
* given node pair and amount.
*
* @generated from protobuf rpc: QueryProbability(routerrpc.QueryProbabilityRequest) returns (routerrpc.QueryProbabilityResponse);
* @generated from protobuf rpc: QueryProbability
*/
queryProbability(input: QueryProbabilityRequest, options?: RpcOptions): UnaryCall<QueryProbabilityRequest, QueryProbabilityResponse> {
const method = this.methods[11], opt = this._transport.mergeOptions(options);
return stackIntercept<QueryProbabilityRequest, QueryProbabilityResponse>("unary", this._transport, method, opt, input);
}
/**
*
* lncli: `buildroute`
* BuildRoute builds a fully specified route based on a list of hop public
* keys. It retrieves the relevant channel policies from the graph in order to
* calculate the correct fees and time locks.
* Note that LND will use its default final_cltv_delta if no value is supplied.
* Make sure to add the correct final_cltv_delta depending on the invoice
* restriction. Moreover the caller has to make sure to provide the
* payment_addr if the route is paying an invoice which signaled it.
*
* @generated from protobuf rpc: BuildRoute(routerrpc.BuildRouteRequest) returns (routerrpc.BuildRouteResponse);
* @generated from protobuf rpc: BuildRoute
*/
buildRoute(input: BuildRouteRequest, options?: RpcOptions): UnaryCall<BuildRouteRequest, BuildRouteResponse> {
const method = this.methods[12], opt = this._transport.mergeOptions(options);
@ -385,7 +463,7 @@ export class RouterClient implements IRouterClient, ServiceInfo {
* SubscribeHtlcEvents creates a uni-directional stream from the server to
* the client which delivers a stream of htlc events.
*
* @generated from protobuf rpc: SubscribeHtlcEvents(routerrpc.SubscribeHtlcEventsRequest) returns (stream routerrpc.HtlcEvent);
* @generated from protobuf rpc: SubscribeHtlcEvents
*/
subscribeHtlcEvents(input: SubscribeHtlcEventsRequest, options?: RpcOptions): ServerStreamingCall<SubscribeHtlcEventsRequest, HtlcEvent> {
const method = this.methods[13], opt = this._transport.mergeOptions(options);
@ -398,7 +476,7 @@ export class RouterClient implements IRouterClient, ServiceInfo {
* returns a stream of payment status updates.
*
* @deprecated
* @generated from protobuf rpc: SendPayment(routerrpc.SendPaymentRequest) returns (stream routerrpc.PaymentStatus);
* @generated from protobuf rpc: SendPayment
*/
sendPayment(input: SendPaymentRequest, options?: RpcOptions): ServerStreamingCall<SendPaymentRequest, PaymentStatus> {
const method = this.methods[14], opt = this._transport.mergeOptions(options);
@ -410,7 +488,7 @@ export class RouterClient implements IRouterClient, ServiceInfo {
* the payment identified by the payment hash.
*
* @deprecated
* @generated from protobuf rpc: TrackPayment(routerrpc.TrackPaymentRequest) returns (stream routerrpc.PaymentStatus);
* @generated from protobuf rpc: TrackPayment
*/
trackPayment(input: TrackPaymentRequest, options?: RpcOptions): ServerStreamingCall<TrackPaymentRequest, PaymentStatus> {
const method = this.methods[15], opt = this._transport.mergeOptions(options);
@ -424,23 +502,51 @@ export class RouterClient implements IRouterClient, ServiceInfo {
* In case of interception, the htlc can be either settled, cancelled or
* resumed later by using the ResolveHoldForward endpoint.
*
* @generated from protobuf rpc: HtlcInterceptor(stream routerrpc.ForwardHtlcInterceptResponse) returns (stream routerrpc.ForwardHtlcInterceptRequest);
* @generated from protobuf rpc: HtlcInterceptor
*/
htlcInterceptor(options?: RpcOptions): DuplexStreamingCall<ForwardHtlcInterceptResponse, ForwardHtlcInterceptRequest> {
const method = this.methods[16], opt = this._transport.mergeOptions(options);
return stackIntercept<ForwardHtlcInterceptResponse, ForwardHtlcInterceptRequest>("duplex", this._transport, method, opt);
}
/**
*
* lncli: `updatechanstatus`
* UpdateChanStatus attempts to manually set the state of a channel
* (enabled, disabled, or auto). A manual "disable" request will cause the
* channel to stay disabled until a subsequent manual request of either
* "enable" or "auto".
*
* @generated from protobuf rpc: UpdateChanStatus(routerrpc.UpdateChanStatusRequest) returns (routerrpc.UpdateChanStatusResponse);
* @generated from protobuf rpc: UpdateChanStatus
*/
updateChanStatus(input: UpdateChanStatusRequest, options?: RpcOptions): UnaryCall<UpdateChanStatusRequest, UpdateChanStatusResponse> {
const method = this.methods[17], opt = this._transport.mergeOptions(options);
return stackIntercept<UpdateChanStatusRequest, UpdateChanStatusResponse>("unary", this._transport, method, opt, input);
}
/**
*
* XAddLocalChanAliases is an experimental API that creates a set of new
* channel SCID alias mappings. The final total set of aliases in the manager
* after the add operation is returned. This is only a locally stored alias,
* and will not be communicated to the channel peer via any message. Therefore,
* routing over such an alias will only work if the peer also calls this same
* RPC on their end. If an alias already exists, an error is returned
*
* @generated from protobuf rpc: XAddLocalChanAliases
*/
xAddLocalChanAliases(input: AddAliasesRequest, options?: RpcOptions): UnaryCall<AddAliasesRequest, AddAliasesResponse> {
const method = this.methods[18], opt = this._transport.mergeOptions(options);
return stackIntercept<AddAliasesRequest, AddAliasesResponse>("unary", this._transport, method, opt, input);
}
/**
*
* XDeleteLocalChanAliases is an experimental API that deletes a set of alias
* mappings. The final total set of aliases in the manager after the delete
* operation is returned. The deletion will not be communicated to the channel
* peer via any message.
*
* @generated from protobuf rpc: XDeleteLocalChanAliases
*/
xDeleteLocalChanAliases(input: DeleteAliasesRequest, options?: RpcOptions): UnaryCall<DeleteAliasesRequest, DeleteAliasesResponse> {
const method = this.methods[19], opt = this._transport.mergeOptions(options);
return stackIntercept<DeleteAliasesRequest, DeleteAliasesResponse>("unary", this._transport, method, opt, input);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.8.1
// @generated by protobuf-ts 2.11.1
// @generated from protobuf file "signer.proto" (package "signrpc", syntax proto3)
// tslint:disable
import type { RpcTransport } from "@protobuf-ts/runtime-rpc";
@ -46,7 +46,7 @@ export interface ISignerClient {
* If we are unable to sign using the specified keys, then an error will be
* returned.
*
* @generated from protobuf rpc: SignOutputRaw(signrpc.SignReq) returns (signrpc.SignResp);
* @generated from protobuf rpc: SignOutputRaw
*/
signOutputRaw(input: SignReq, options?: RpcOptions): UnaryCall<SignReq, SignResp>;
/**
@ -62,7 +62,7 @@ export interface ISignerClient {
* in the TxOut field, the value in that same field, and finally the input
* index.
*
* @generated from protobuf rpc: ComputeInputScript(signrpc.SignReq) returns (signrpc.InputScriptResp);
* @generated from protobuf rpc: ComputeInputScript
*/
computeInputScript(input: SignReq, options?: RpcOptions): UnaryCall<SignReq, InputScriptResp>;
/**
@ -73,7 +73,7 @@ export interface ISignerClient {
* The main difference to SignMessage in the main RPC is that a specific key is
* used to sign the message instead of the node identity private key.
*
* @generated from protobuf rpc: SignMessage(signrpc.SignMessageReq) returns (signrpc.SignMessageResp);
* @generated from protobuf rpc: SignMessage
*/
signMessage(input: SignMessageReq, options?: RpcOptions): UnaryCall<SignMessageReq, SignMessageResp>;
/**
@ -84,7 +84,7 @@ export interface ISignerClient {
* The main difference to VerifyMessage in the main RPC is that the public key
* used to sign the message does not have to be a node known to the network.
*
* @generated from protobuf rpc: VerifyMessage(signrpc.VerifyMessageReq) returns (signrpc.VerifyMessageResp);
* @generated from protobuf rpc: VerifyMessage
*/
verifyMessage(input: VerifyMessageReq, options?: RpcOptions): UnaryCall<VerifyMessageReq, VerifyMessageResp>;
/**
@ -98,7 +98,7 @@ export interface ISignerClient {
* The resulting shared public key is serialized in the compressed format and
* hashed with sha256, resulting in the final key length of 256bit.
*
* @generated from protobuf rpc: DeriveSharedKey(signrpc.SharedKeyRequest) returns (signrpc.SharedKeyResponse);
* @generated from protobuf rpc: DeriveSharedKey
*/
deriveSharedKey(input: SharedKeyRequest, options?: RpcOptions): UnaryCall<SharedKeyRequest, SharedKeyResponse>;
/**
@ -115,7 +115,7 @@ export interface ISignerClient {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2CombineKeys(signrpc.MuSig2CombineKeysRequest) returns (signrpc.MuSig2CombineKeysResponse);
* @generated from protobuf rpc: MuSig2CombineKeys
*/
muSig2CombineKeys(input: MuSig2CombineKeysRequest, options?: RpcOptions): UnaryCall<MuSig2CombineKeysRequest, MuSig2CombineKeysResponse>;
/**
@ -131,7 +131,7 @@ export interface ISignerClient {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2CreateSession(signrpc.MuSig2SessionRequest) returns (signrpc.MuSig2SessionResponse);
* @generated from protobuf rpc: MuSig2CreateSession
*/
muSig2CreateSession(input: MuSig2SessionRequest, options?: RpcOptions): UnaryCall<MuSig2SessionRequest, MuSig2SessionResponse>;
/**
@ -144,7 +144,7 @@ export interface ISignerClient {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2RegisterNonces(signrpc.MuSig2RegisterNoncesRequest) returns (signrpc.MuSig2RegisterNoncesResponse);
* @generated from protobuf rpc: MuSig2RegisterNonces
*/
muSig2RegisterNonces(input: MuSig2RegisterNoncesRequest, options?: RpcOptions): UnaryCall<MuSig2RegisterNoncesRequest, MuSig2RegisterNoncesResponse>;
/**
@ -160,7 +160,7 @@ export interface ISignerClient {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2Sign(signrpc.MuSig2SignRequest) returns (signrpc.MuSig2SignResponse);
* @generated from protobuf rpc: MuSig2Sign
*/
muSig2Sign(input: MuSig2SignRequest, options?: RpcOptions): UnaryCall<MuSig2SignRequest, MuSig2SignResponse>;
/**
@ -174,7 +174,7 @@ export interface ISignerClient {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2CombineSig(signrpc.MuSig2CombineSigRequest) returns (signrpc.MuSig2CombineSigResponse);
* @generated from protobuf rpc: MuSig2CombineSig
*/
muSig2CombineSig(input: MuSig2CombineSigRequest, options?: RpcOptions): UnaryCall<MuSig2CombineSigRequest, MuSig2CombineSigResponse>;
/**
@ -187,7 +187,7 @@ export interface ISignerClient {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2Cleanup(signrpc.MuSig2CleanupRequest) returns (signrpc.MuSig2CleanupResponse);
* @generated from protobuf rpc: MuSig2Cleanup
*/
muSig2Cleanup(input: MuSig2CleanupRequest, options?: RpcOptions): UnaryCall<MuSig2CleanupRequest, MuSig2CleanupResponse>;
}
@ -214,7 +214,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* If we are unable to sign using the specified keys, then an error will be
* returned.
*
* @generated from protobuf rpc: SignOutputRaw(signrpc.SignReq) returns (signrpc.SignResp);
* @generated from protobuf rpc: SignOutputRaw
*/
signOutputRaw(input: SignReq, options?: RpcOptions): UnaryCall<SignReq, SignResp> {
const method = this.methods[0], opt = this._transport.mergeOptions(options);
@ -233,7 +233,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* in the TxOut field, the value in that same field, and finally the input
* index.
*
* @generated from protobuf rpc: ComputeInputScript(signrpc.SignReq) returns (signrpc.InputScriptResp);
* @generated from protobuf rpc: ComputeInputScript
*/
computeInputScript(input: SignReq, options?: RpcOptions): UnaryCall<SignReq, InputScriptResp> {
const method = this.methods[1], opt = this._transport.mergeOptions(options);
@ -247,7 +247,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* The main difference to SignMessage in the main RPC is that a specific key is
* used to sign the message instead of the node identity private key.
*
* @generated from protobuf rpc: SignMessage(signrpc.SignMessageReq) returns (signrpc.SignMessageResp);
* @generated from protobuf rpc: SignMessage
*/
signMessage(input: SignMessageReq, options?: RpcOptions): UnaryCall<SignMessageReq, SignMessageResp> {
const method = this.methods[2], opt = this._transport.mergeOptions(options);
@ -261,7 +261,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* The main difference to VerifyMessage in the main RPC is that the public key
* used to sign the message does not have to be a node known to the network.
*
* @generated from protobuf rpc: VerifyMessage(signrpc.VerifyMessageReq) returns (signrpc.VerifyMessageResp);
* @generated from protobuf rpc: VerifyMessage
*/
verifyMessage(input: VerifyMessageReq, options?: RpcOptions): UnaryCall<VerifyMessageReq, VerifyMessageResp> {
const method = this.methods[3], opt = this._transport.mergeOptions(options);
@ -278,7 +278,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* The resulting shared public key is serialized in the compressed format and
* hashed with sha256, resulting in the final key length of 256bit.
*
* @generated from protobuf rpc: DeriveSharedKey(signrpc.SharedKeyRequest) returns (signrpc.SharedKeyResponse);
* @generated from protobuf rpc: DeriveSharedKey
*/
deriveSharedKey(input: SharedKeyRequest, options?: RpcOptions): UnaryCall<SharedKeyRequest, SharedKeyResponse> {
const method = this.methods[4], opt = this._transport.mergeOptions(options);
@ -298,7 +298,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2CombineKeys(signrpc.MuSig2CombineKeysRequest) returns (signrpc.MuSig2CombineKeysResponse);
* @generated from protobuf rpc: MuSig2CombineKeys
*/
muSig2CombineKeys(input: MuSig2CombineKeysRequest, options?: RpcOptions): UnaryCall<MuSig2CombineKeysRequest, MuSig2CombineKeysResponse> {
const method = this.methods[5], opt = this._transport.mergeOptions(options);
@ -317,7 +317,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2CreateSession(signrpc.MuSig2SessionRequest) returns (signrpc.MuSig2SessionResponse);
* @generated from protobuf rpc: MuSig2CreateSession
*/
muSig2CreateSession(input: MuSig2SessionRequest, options?: RpcOptions): UnaryCall<MuSig2SessionRequest, MuSig2SessionResponse> {
const method = this.methods[6], opt = this._transport.mergeOptions(options);
@ -333,7 +333,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2RegisterNonces(signrpc.MuSig2RegisterNoncesRequest) returns (signrpc.MuSig2RegisterNoncesResponse);
* @generated from protobuf rpc: MuSig2RegisterNonces
*/
muSig2RegisterNonces(input: MuSig2RegisterNoncesRequest, options?: RpcOptions): UnaryCall<MuSig2RegisterNoncesRequest, MuSig2RegisterNoncesResponse> {
const method = this.methods[7], opt = this._transport.mergeOptions(options);
@ -352,7 +352,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2Sign(signrpc.MuSig2SignRequest) returns (signrpc.MuSig2SignResponse);
* @generated from protobuf rpc: MuSig2Sign
*/
muSig2Sign(input: MuSig2SignRequest, options?: RpcOptions): UnaryCall<MuSig2SignRequest, MuSig2SignResponse> {
const method = this.methods[8], opt = this._transport.mergeOptions(options);
@ -369,7 +369,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2CombineSig(signrpc.MuSig2CombineSigRequest) returns (signrpc.MuSig2CombineSigResponse);
* @generated from protobuf rpc: MuSig2CombineSig
*/
muSig2CombineSig(input: MuSig2CombineSigRequest, options?: RpcOptions): UnaryCall<MuSig2CombineSigRequest, MuSig2CombineSigResponse> {
const method = this.methods[9], opt = this._transport.mergeOptions(options);
@ -385,7 +385,7 @@ export class SignerClient implements ISignerClient, ServiceInfo {
* considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
* releases. Backward compatibility is not guaranteed!
*
* @generated from protobuf rpc: MuSig2Cleanup(signrpc.MuSig2CleanupRequest) returns (signrpc.MuSig2CleanupResponse);
* @generated from protobuf rpc: MuSig2Cleanup
*/
muSig2Cleanup(input: MuSig2CleanupRequest, options?: RpcOptions): UnaryCall<MuSig2CleanupRequest, MuSig2CleanupResponse> {
const method = this.methods[10], opt = this._transport.mergeOptions(options);

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.8.1
// @generated by protobuf-ts 2.11.1
// @generated from protobuf file "walletkit.proto" (package "walletrpc", syntax proto3)
// tslint:disable
import type { RpcTransport } from "@protobuf-ts/runtime-rpc";
@ -43,7 +43,7 @@ import type { RequiredReserveResponse } from "./walletkit.js";
import type { RequiredReserveRequest } from "./walletkit.js";
import type { ListAccountsResponse } from "./walletkit.js";
import type { ListAccountsRequest } from "./walletkit.js";
import type { Transaction } from "./lightning";
import type { Transaction } from "./lightning.js";
import type { GetTransactionRequest } from "./walletkit.js";
import type { AddrResponse } from "./walletkit.js";
import type { AddrRequest } from "./walletkit.js";
@ -92,7 +92,7 @@ export interface IWalletKitClient {
* default, all utxos are listed. To list only the unconfirmed utxos, set
* the unconfirmed_only to true.
*
* @generated from protobuf rpc: ListUnspent(walletrpc.ListUnspentRequest) returns (walletrpc.ListUnspentResponse);
* @generated from protobuf rpc: ListUnspent
*/
listUnspent(input: ListUnspentRequest, options?: RpcOptions): UnaryCall<ListUnspentRequest, ListUnspentResponse>;
/**
@ -103,7 +103,7 @@ export interface IWalletKitClient {
* successive invocations of this RPC. Outputs can be unlocked before their
* expiration through `ReleaseOutput`.
*
* @generated from protobuf rpc: LeaseOutput(walletrpc.LeaseOutputRequest) returns (walletrpc.LeaseOutputResponse);
* @generated from protobuf rpc: LeaseOutput
*/
leaseOutput(input: LeaseOutputRequest, options?: RpcOptions): UnaryCall<LeaseOutputRequest, LeaseOutputResponse>;
/**
@ -112,14 +112,14 @@ export interface IWalletKitClient {
* selection if it remains unspent. The ID should match the one used to
* originally lock the output.
*
* @generated from protobuf rpc: ReleaseOutput(walletrpc.ReleaseOutputRequest) returns (walletrpc.ReleaseOutputResponse);
* @generated from protobuf rpc: ReleaseOutput
*/
releaseOutput(input: ReleaseOutputRequest, options?: RpcOptions): UnaryCall<ReleaseOutputRequest, ReleaseOutputResponse>;
/**
* lncli: `wallet listleases`
* ListLeases lists all currently locked utxos.
*
* @generated from protobuf rpc: ListLeases(walletrpc.ListLeasesRequest) returns (walletrpc.ListLeasesResponse);
* @generated from protobuf rpc: ListLeases
*/
listLeases(input: ListLeasesRequest, options?: RpcOptions): UnaryCall<ListLeasesRequest, ListLeasesResponse>;
/**
@ -128,7 +128,7 @@ export interface IWalletKitClient {
* (account in BIP43) specified. This method should return the next external
* child within this branch.
*
* @generated from protobuf rpc: DeriveNextKey(walletrpc.KeyReq) returns (signrpc.KeyDescriptor);
* @generated from protobuf rpc: DeriveNextKey
*/
deriveNextKey(input: KeyReq, options?: RpcOptions): UnaryCall<KeyReq, KeyDescriptor>;
/**
@ -136,21 +136,21 @@ export interface IWalletKitClient {
* DeriveKey attempts to derive an arbitrary key specified by the passed
* KeyLocator.
*
* @generated from protobuf rpc: DeriveKey(signrpc.KeyLocator) returns (signrpc.KeyDescriptor);
* @generated from protobuf rpc: DeriveKey
*/
deriveKey(input: KeyLocator, options?: RpcOptions): UnaryCall<KeyLocator, KeyDescriptor>;
/**
*
* NextAddr returns the next unused address within the wallet.
*
* @generated from protobuf rpc: NextAddr(walletrpc.AddrRequest) returns (walletrpc.AddrResponse);
* @generated from protobuf rpc: NextAddr
*/
nextAddr(input: AddrRequest, options?: RpcOptions): UnaryCall<AddrRequest, AddrResponse>;
/**
* lncli: `wallet gettx`
* GetTransaction returns details for a transaction found in the wallet.
*
* @generated from protobuf rpc: GetTransaction(walletrpc.GetTransactionRequest) returns (lnrpc.Transaction);
* @generated from protobuf rpc: GetTransaction
*/
getTransaction(input: GetTransactionRequest, options?: RpcOptions): UnaryCall<GetTransactionRequest, Transaction>;
/**
@ -159,7 +159,7 @@ export interface IWalletKitClient {
* name and key scope filter can be provided to filter through all of the
* wallet accounts and return only those matching.
*
* @generated from protobuf rpc: ListAccounts(walletrpc.ListAccountsRequest) returns (walletrpc.ListAccountsResponse);
* @generated from protobuf rpc: ListAccounts
*/
listAccounts(input: ListAccountsRequest, options?: RpcOptions): UnaryCall<ListAccountsRequest, ListAccountsResponse>;
/**
@ -168,7 +168,7 @@ export interface IWalletKitClient {
* in the wallet in order to fee bump anchor channels if necessary. The value
* scales with the number of public anchor channels but is capped at a maximum.
*
* @generated from protobuf rpc: RequiredReserve(walletrpc.RequiredReserveRequest) returns (walletrpc.RequiredReserveResponse);
* @generated from protobuf rpc: RequiredReserve
*/
requiredReserve(input: RequiredReserveRequest, options?: RpcOptions): UnaryCall<RequiredReserveRequest, RequiredReserveResponse>;
/**
@ -177,7 +177,7 @@ export interface IWalletKitClient {
* account name filter can be provided to filter through all of the
* wallet accounts and return the addresses of only those matching.
*
* @generated from protobuf rpc: ListAddresses(walletrpc.ListAddressesRequest) returns (walletrpc.ListAddressesResponse);
* @generated from protobuf rpc: ListAddresses
*/
listAddresses(input: ListAddressesRequest, options?: RpcOptions): UnaryCall<ListAddressesRequest, ListAddressesResponse>;
/**
@ -195,7 +195,7 @@ export interface IWalletKitClient {
* For P2TR addresses this represents a special case. ECDSA is used to create
* a compact signature which makes the public key of the signature recoverable.
*
* @generated from protobuf rpc: SignMessageWithAddr(walletrpc.SignMessageWithAddrRequest) returns (walletrpc.SignMessageWithAddrResponse);
* @generated from protobuf rpc: SignMessageWithAddr
*/
signMessageWithAddr(input: SignMessageWithAddrRequest, options?: RpcOptions): UnaryCall<SignMessageWithAddrRequest, SignMessageWithAddrResponse>;
/**
@ -220,7 +220,7 @@ export interface IWalletKitClient {
* taproot key. The compact ECDSA signature format was used because there
* are still no known compact signature schemes for schnorr signatures.
*
* @generated from protobuf rpc: VerifyMessageWithAddr(walletrpc.VerifyMessageWithAddrRequest) returns (walletrpc.VerifyMessageWithAddrResponse);
* @generated from protobuf rpc: VerifyMessageWithAddr
*/
verifyMessageWithAddr(input: VerifyMessageWithAddrRequest, options?: RpcOptions): UnaryCall<VerifyMessageWithAddrRequest, VerifyMessageWithAddrResponse>;
/**
@ -249,7 +249,7 @@ export interface IWalletKitClient {
* detected by lnd if they happen after the import. Rescans to detect past
* events will be supported later on.
*
* @generated from protobuf rpc: ImportAccount(walletrpc.ImportAccountRequest) returns (walletrpc.ImportAccountResponse);
* @generated from protobuf rpc: ImportAccount
*/
importAccount(input: ImportAccountRequest, options?: RpcOptions): UnaryCall<ImportAccountRequest, ImportAccountResponse>;
/**
@ -264,7 +264,7 @@ export interface IWalletKitClient {
* they happen after the import. Rescans to detect past events will be
* supported later on.
*
* @generated from protobuf rpc: ImportPublicKey(walletrpc.ImportPublicKeyRequest) returns (walletrpc.ImportPublicKeyResponse);
* @generated from protobuf rpc: ImportPublicKey
*/
importPublicKey(input: ImportPublicKeyRequest, options?: RpcOptions): UnaryCall<ImportPublicKeyRequest, ImportPublicKeyResponse>;
/**
@ -281,7 +281,7 @@ export interface IWalletKitClient {
* NOTE: Taproot keys imported through this RPC currently _cannot_ be used for
* funding PSBTs. Only tracking the balance and UTXOs is currently supported.
*
* @generated from protobuf rpc: ImportTapscript(walletrpc.ImportTapscriptRequest) returns (walletrpc.ImportTapscriptResponse);
* @generated from protobuf rpc: ImportTapscript
*/
importTapscript(input: ImportTapscriptRequest, options?: RpcOptions): UnaryCall<ImportTapscriptRequest, ImportTapscriptResponse>;
/**
@ -291,7 +291,7 @@ export interface IWalletKitClient {
* attempt to re-broadcast the transaction on start up, until it enters the
* chain.
*
* @generated from protobuf rpc: PublishTransaction(walletrpc.Transaction) returns (walletrpc.PublishResponse);
* @generated from protobuf rpc: PublishTransaction
*/
publishTransaction(input: Transaction$, options?: RpcOptions): UnaryCall<Transaction$, PublishResponse>;
/**
@ -299,7 +299,7 @@ export interface IWalletKitClient {
* RemoveTransaction attempts to remove the provided transaction from the
* internal transaction store of the wallet.
*
* @generated from protobuf rpc: RemoveTransaction(walletrpc.GetTransactionRequest) returns (walletrpc.RemoveTransactionResponse);
* @generated from protobuf rpc: RemoveTransaction
*/
removeTransaction(input: GetTransactionRequest, options?: RpcOptions): UnaryCall<GetTransactionRequest, RemoveTransactionResponse>;
/**
@ -308,7 +308,7 @@ export interface IWalletKitClient {
* allows the caller to create a transaction that sends to several outputs at
* once. This is ideal when wanting to batch create a set of transactions.
*
* @generated from protobuf rpc: SendOutputs(walletrpc.SendOutputsRequest) returns (walletrpc.SendOutputsResponse);
* @generated from protobuf rpc: SendOutputs
*/
sendOutputs(input: SendOutputsRequest, options?: RpcOptions): UnaryCall<SendOutputsRequest, SendOutputsResponse>;
/**
@ -317,7 +317,7 @@ export interface IWalletKitClient {
* determine the fee (in sat/kw) to attach to a transaction in order to
* achieve the confirmation target.
*
* @generated from protobuf rpc: EstimateFee(walletrpc.EstimateFeeRequest) returns (walletrpc.EstimateFeeResponse);
* @generated from protobuf rpc: EstimateFee
*/
estimateFee(input: EstimateFeeRequest, options?: RpcOptions): UnaryCall<EstimateFeeRequest, EstimateFeeResponse>;
/**
@ -331,7 +331,7 @@ export interface IWalletKitClient {
* remain supported. This is an advanced API that depends on the internals of
* the UtxoSweeper, so things may change.
*
* @generated from protobuf rpc: PendingSweeps(walletrpc.PendingSweepsRequest) returns (walletrpc.PendingSweepsResponse);
* @generated from protobuf rpc: PendingSweeps
*/
pendingSweeps(input: PendingSweepsRequest, options?: RpcOptions): UnaryCall<PendingSweepsRequest, PendingSweepsResponse>;
/**
@ -365,7 +365,7 @@ export interface IWalletKitClient {
* done by specifying an outpoint within the low fee transaction that is under
* the control of the wallet.
*
* @generated from protobuf rpc: BumpFee(walletrpc.BumpFeeRequest) returns (walletrpc.BumpFeeResponse);
* @generated from protobuf rpc: BumpFee
*/
bumpFee(input: BumpFeeRequest, options?: RpcOptions): UnaryCall<BumpFeeRequest, BumpFeeResponse>;
/**
@ -373,7 +373,7 @@ export interface IWalletKitClient {
* BumpForceCloseFee is an endpoint that allows users to bump the fee of a
* channel force close. This only works for channels with option_anchors.
*
* @generated from protobuf rpc: BumpForceCloseFee(walletrpc.BumpForceCloseFeeRequest) returns (walletrpc.BumpForceCloseFeeResponse);
* @generated from protobuf rpc: BumpForceCloseFee
*/
bumpForceCloseFee(input: BumpForceCloseFeeRequest, options?: RpcOptions): UnaryCall<BumpForceCloseFeeRequest, BumpForceCloseFeeResponse>;
/**
@ -382,7 +382,7 @@ export interface IWalletKitClient {
* Note that these sweeps may not be confirmed yet, as we record sweeps on
* broadcast, not confirmation.
*
* @generated from protobuf rpc: ListSweeps(walletrpc.ListSweepsRequest) returns (walletrpc.ListSweepsResponse);
* @generated from protobuf rpc: ListSweeps
*/
listSweeps(input: ListSweepsRequest, options?: RpcOptions): UnaryCall<ListSweepsRequest, ListSweepsResponse>;
/**
@ -392,7 +392,7 @@ export interface IWalletKitClient {
* overwrite the existing transaction label. Labels must not be empty, and
* cannot exceed 500 characters.
*
* @generated from protobuf rpc: LabelTransaction(walletrpc.LabelTransactionRequest) returns (walletrpc.LabelTransactionResponse);
* @generated from protobuf rpc: LabelTransaction
*/
labelTransaction(input: LabelTransactionRequest, options?: RpcOptions): UnaryCall<LabelTransactionRequest, LabelTransactionResponse>;
/**
@ -426,7 +426,7 @@ export interface IWalletKitClient {
* publishing the transaction) or to unlock/release the locked UTXOs in case of
* an error on the caller's side.
*
* @generated from protobuf rpc: FundPsbt(walletrpc.FundPsbtRequest) returns (walletrpc.FundPsbtResponse);
* @generated from protobuf rpc: FundPsbt
*/
fundPsbt(input: FundPsbtRequest, options?: RpcOptions): UnaryCall<FundPsbtRequest, FundPsbtResponse>;
/**
@ -443,7 +443,7 @@ export interface IWalletKitClient {
* input/output/fee value validation, PSBT finalization). Any input that is
* incomplete will be skipped.
*
* @generated from protobuf rpc: SignPsbt(walletrpc.SignPsbtRequest) returns (walletrpc.SignPsbtResponse);
* @generated from protobuf rpc: SignPsbt
*/
signPsbt(input: SignPsbtRequest, options?: RpcOptions): UnaryCall<SignPsbtRequest, SignPsbtResponse>;
/**
@ -460,7 +460,7 @@ export interface IWalletKitClient {
* caller's responsibility to either publish the transaction on success or
* unlock/release any locked UTXOs in case of an error in this method.
*
* @generated from protobuf rpc: FinalizePsbt(walletrpc.FinalizePsbtRequest) returns (walletrpc.FinalizePsbtResponse);
* @generated from protobuf rpc: FinalizePsbt
*/
finalizePsbt(input: FinalizePsbtRequest, options?: RpcOptions): UnaryCall<FinalizePsbtRequest, FinalizePsbtResponse>;
}
@ -500,7 +500,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* default, all utxos are listed. To list only the unconfirmed utxos, set
* the unconfirmed_only to true.
*
* @generated from protobuf rpc: ListUnspent(walletrpc.ListUnspentRequest) returns (walletrpc.ListUnspentResponse);
* @generated from protobuf rpc: ListUnspent
*/
listUnspent(input: ListUnspentRequest, options?: RpcOptions): UnaryCall<ListUnspentRequest, ListUnspentResponse> {
const method = this.methods[0], opt = this._transport.mergeOptions(options);
@ -514,7 +514,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* successive invocations of this RPC. Outputs can be unlocked before their
* expiration through `ReleaseOutput`.
*
* @generated from protobuf rpc: LeaseOutput(walletrpc.LeaseOutputRequest) returns (walletrpc.LeaseOutputResponse);
* @generated from protobuf rpc: LeaseOutput
*/
leaseOutput(input: LeaseOutputRequest, options?: RpcOptions): UnaryCall<LeaseOutputRequest, LeaseOutputResponse> {
const method = this.methods[1], opt = this._transport.mergeOptions(options);
@ -526,7 +526,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* selection if it remains unspent. The ID should match the one used to
* originally lock the output.
*
* @generated from protobuf rpc: ReleaseOutput(walletrpc.ReleaseOutputRequest) returns (walletrpc.ReleaseOutputResponse);
* @generated from protobuf rpc: ReleaseOutput
*/
releaseOutput(input: ReleaseOutputRequest, options?: RpcOptions): UnaryCall<ReleaseOutputRequest, ReleaseOutputResponse> {
const method = this.methods[2], opt = this._transport.mergeOptions(options);
@ -536,7 +536,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* lncli: `wallet listleases`
* ListLeases lists all currently locked utxos.
*
* @generated from protobuf rpc: ListLeases(walletrpc.ListLeasesRequest) returns (walletrpc.ListLeasesResponse);
* @generated from protobuf rpc: ListLeases
*/
listLeases(input: ListLeasesRequest, options?: RpcOptions): UnaryCall<ListLeasesRequest, ListLeasesResponse> {
const method = this.methods[3], opt = this._transport.mergeOptions(options);
@ -548,7 +548,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* (account in BIP43) specified. This method should return the next external
* child within this branch.
*
* @generated from protobuf rpc: DeriveNextKey(walletrpc.KeyReq) returns (signrpc.KeyDescriptor);
* @generated from protobuf rpc: DeriveNextKey
*/
deriveNextKey(input: KeyReq, options?: RpcOptions): UnaryCall<KeyReq, KeyDescriptor> {
const method = this.methods[4], opt = this._transport.mergeOptions(options);
@ -559,7 +559,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* DeriveKey attempts to derive an arbitrary key specified by the passed
* KeyLocator.
*
* @generated from protobuf rpc: DeriveKey(signrpc.KeyLocator) returns (signrpc.KeyDescriptor);
* @generated from protobuf rpc: DeriveKey
*/
deriveKey(input: KeyLocator, options?: RpcOptions): UnaryCall<KeyLocator, KeyDescriptor> {
const method = this.methods[5], opt = this._transport.mergeOptions(options);
@ -569,7 +569,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
*
* NextAddr returns the next unused address within the wallet.
*
* @generated from protobuf rpc: NextAddr(walletrpc.AddrRequest) returns (walletrpc.AddrResponse);
* @generated from protobuf rpc: NextAddr
*/
nextAddr(input: AddrRequest, options?: RpcOptions): UnaryCall<AddrRequest, AddrResponse> {
const method = this.methods[6], opt = this._transport.mergeOptions(options);
@ -579,7 +579,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* lncli: `wallet gettx`
* GetTransaction returns details for a transaction found in the wallet.
*
* @generated from protobuf rpc: GetTransaction(walletrpc.GetTransactionRequest) returns (lnrpc.Transaction);
* @generated from protobuf rpc: GetTransaction
*/
getTransaction(input: GetTransactionRequest, options?: RpcOptions): UnaryCall<GetTransactionRequest, Transaction> {
const method = this.methods[7], opt = this._transport.mergeOptions(options);
@ -591,7 +591,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* name and key scope filter can be provided to filter through all of the
* wallet accounts and return only those matching.
*
* @generated from protobuf rpc: ListAccounts(walletrpc.ListAccountsRequest) returns (walletrpc.ListAccountsResponse);
* @generated from protobuf rpc: ListAccounts
*/
listAccounts(input: ListAccountsRequest, options?: RpcOptions): UnaryCall<ListAccountsRequest, ListAccountsResponse> {
const method = this.methods[8], opt = this._transport.mergeOptions(options);
@ -603,7 +603,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* in the wallet in order to fee bump anchor channels if necessary. The value
* scales with the number of public anchor channels but is capped at a maximum.
*
* @generated from protobuf rpc: RequiredReserve(walletrpc.RequiredReserveRequest) returns (walletrpc.RequiredReserveResponse);
* @generated from protobuf rpc: RequiredReserve
*/
requiredReserve(input: RequiredReserveRequest, options?: RpcOptions): UnaryCall<RequiredReserveRequest, RequiredReserveResponse> {
const method = this.methods[9], opt = this._transport.mergeOptions(options);
@ -615,7 +615,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* account name filter can be provided to filter through all of the
* wallet accounts and return the addresses of only those matching.
*
* @generated from protobuf rpc: ListAddresses(walletrpc.ListAddressesRequest) returns (walletrpc.ListAddressesResponse);
* @generated from protobuf rpc: ListAddresses
*/
listAddresses(input: ListAddressesRequest, options?: RpcOptions): UnaryCall<ListAddressesRequest, ListAddressesResponse> {
const method = this.methods[10], opt = this._transport.mergeOptions(options);
@ -636,7 +636,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* For P2TR addresses this represents a special case. ECDSA is used to create
* a compact signature which makes the public key of the signature recoverable.
*
* @generated from protobuf rpc: SignMessageWithAddr(walletrpc.SignMessageWithAddrRequest) returns (walletrpc.SignMessageWithAddrResponse);
* @generated from protobuf rpc: SignMessageWithAddr
*/
signMessageWithAddr(input: SignMessageWithAddrRequest, options?: RpcOptions): UnaryCall<SignMessageWithAddrRequest, SignMessageWithAddrResponse> {
const method = this.methods[11], opt = this._transport.mergeOptions(options);
@ -664,7 +664,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* taproot key. The compact ECDSA signature format was used because there
* are still no known compact signature schemes for schnorr signatures.
*
* @generated from protobuf rpc: VerifyMessageWithAddr(walletrpc.VerifyMessageWithAddrRequest) returns (walletrpc.VerifyMessageWithAddrResponse);
* @generated from protobuf rpc: VerifyMessageWithAddr
*/
verifyMessageWithAddr(input: VerifyMessageWithAddrRequest, options?: RpcOptions): UnaryCall<VerifyMessageWithAddrRequest, VerifyMessageWithAddrResponse> {
const method = this.methods[12], opt = this._transport.mergeOptions(options);
@ -696,7 +696,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* detected by lnd if they happen after the import. Rescans to detect past
* events will be supported later on.
*
* @generated from protobuf rpc: ImportAccount(walletrpc.ImportAccountRequest) returns (walletrpc.ImportAccountResponse);
* @generated from protobuf rpc: ImportAccount
*/
importAccount(input: ImportAccountRequest, options?: RpcOptions): UnaryCall<ImportAccountRequest, ImportAccountResponse> {
const method = this.methods[13], opt = this._transport.mergeOptions(options);
@ -714,7 +714,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* they happen after the import. Rescans to detect past events will be
* supported later on.
*
* @generated from protobuf rpc: ImportPublicKey(walletrpc.ImportPublicKeyRequest) returns (walletrpc.ImportPublicKeyResponse);
* @generated from protobuf rpc: ImportPublicKey
*/
importPublicKey(input: ImportPublicKeyRequest, options?: RpcOptions): UnaryCall<ImportPublicKeyRequest, ImportPublicKeyResponse> {
const method = this.methods[14], opt = this._transport.mergeOptions(options);
@ -734,7 +734,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* NOTE: Taproot keys imported through this RPC currently _cannot_ be used for
* funding PSBTs. Only tracking the balance and UTXOs is currently supported.
*
* @generated from protobuf rpc: ImportTapscript(walletrpc.ImportTapscriptRequest) returns (walletrpc.ImportTapscriptResponse);
* @generated from protobuf rpc: ImportTapscript
*/
importTapscript(input: ImportTapscriptRequest, options?: RpcOptions): UnaryCall<ImportTapscriptRequest, ImportTapscriptResponse> {
const method = this.methods[15], opt = this._transport.mergeOptions(options);
@ -747,7 +747,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* attempt to re-broadcast the transaction on start up, until it enters the
* chain.
*
* @generated from protobuf rpc: PublishTransaction(walletrpc.Transaction) returns (walletrpc.PublishResponse);
* @generated from protobuf rpc: PublishTransaction
*/
publishTransaction(input: Transaction$, options?: RpcOptions): UnaryCall<Transaction$, PublishResponse> {
const method = this.methods[16], opt = this._transport.mergeOptions(options);
@ -758,7 +758,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* RemoveTransaction attempts to remove the provided transaction from the
* internal transaction store of the wallet.
*
* @generated from protobuf rpc: RemoveTransaction(walletrpc.GetTransactionRequest) returns (walletrpc.RemoveTransactionResponse);
* @generated from protobuf rpc: RemoveTransaction
*/
removeTransaction(input: GetTransactionRequest, options?: RpcOptions): UnaryCall<GetTransactionRequest, RemoveTransactionResponse> {
const method = this.methods[17], opt = this._transport.mergeOptions(options);
@ -770,7 +770,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* allows the caller to create a transaction that sends to several outputs at
* once. This is ideal when wanting to batch create a set of transactions.
*
* @generated from protobuf rpc: SendOutputs(walletrpc.SendOutputsRequest) returns (walletrpc.SendOutputsResponse);
* @generated from protobuf rpc: SendOutputs
*/
sendOutputs(input: SendOutputsRequest, options?: RpcOptions): UnaryCall<SendOutputsRequest, SendOutputsResponse> {
const method = this.methods[18], opt = this._transport.mergeOptions(options);
@ -782,7 +782,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* determine the fee (in sat/kw) to attach to a transaction in order to
* achieve the confirmation target.
*
* @generated from protobuf rpc: EstimateFee(walletrpc.EstimateFeeRequest) returns (walletrpc.EstimateFeeResponse);
* @generated from protobuf rpc: EstimateFee
*/
estimateFee(input: EstimateFeeRequest, options?: RpcOptions): UnaryCall<EstimateFeeRequest, EstimateFeeResponse> {
const method = this.methods[19], opt = this._transport.mergeOptions(options);
@ -799,7 +799,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* remain supported. This is an advanced API that depends on the internals of
* the UtxoSweeper, so things may change.
*
* @generated from protobuf rpc: PendingSweeps(walletrpc.PendingSweepsRequest) returns (walletrpc.PendingSweepsResponse);
* @generated from protobuf rpc: PendingSweeps
*/
pendingSweeps(input: PendingSweepsRequest, options?: RpcOptions): UnaryCall<PendingSweepsRequest, PendingSweepsResponse> {
const method = this.methods[20], opt = this._transport.mergeOptions(options);
@ -836,7 +836,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* done by specifying an outpoint within the low fee transaction that is under
* the control of the wallet.
*
* @generated from protobuf rpc: BumpFee(walletrpc.BumpFeeRequest) returns (walletrpc.BumpFeeResponse);
* @generated from protobuf rpc: BumpFee
*/
bumpFee(input: BumpFeeRequest, options?: RpcOptions): UnaryCall<BumpFeeRequest, BumpFeeResponse> {
const method = this.methods[21], opt = this._transport.mergeOptions(options);
@ -847,7 +847,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* BumpForceCloseFee is an endpoint that allows users to bump the fee of a
* channel force close. This only works for channels with option_anchors.
*
* @generated from protobuf rpc: BumpForceCloseFee(walletrpc.BumpForceCloseFeeRequest) returns (walletrpc.BumpForceCloseFeeResponse);
* @generated from protobuf rpc: BumpForceCloseFee
*/
bumpForceCloseFee(input: BumpForceCloseFeeRequest, options?: RpcOptions): UnaryCall<BumpForceCloseFeeRequest, BumpForceCloseFeeResponse> {
const method = this.methods[22], opt = this._transport.mergeOptions(options);
@ -859,7 +859,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* Note that these sweeps may not be confirmed yet, as we record sweeps on
* broadcast, not confirmation.
*
* @generated from protobuf rpc: ListSweeps(walletrpc.ListSweepsRequest) returns (walletrpc.ListSweepsResponse);
* @generated from protobuf rpc: ListSweeps
*/
listSweeps(input: ListSweepsRequest, options?: RpcOptions): UnaryCall<ListSweepsRequest, ListSweepsResponse> {
const method = this.methods[23], opt = this._transport.mergeOptions(options);
@ -872,7 +872,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* overwrite the existing transaction label. Labels must not be empty, and
* cannot exceed 500 characters.
*
* @generated from protobuf rpc: LabelTransaction(walletrpc.LabelTransactionRequest) returns (walletrpc.LabelTransactionResponse);
* @generated from protobuf rpc: LabelTransaction
*/
labelTransaction(input: LabelTransactionRequest, options?: RpcOptions): UnaryCall<LabelTransactionRequest, LabelTransactionResponse> {
const method = this.methods[24], opt = this._transport.mergeOptions(options);
@ -909,7 +909,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* publishing the transaction) or to unlock/release the locked UTXOs in case of
* an error on the caller's side.
*
* @generated from protobuf rpc: FundPsbt(walletrpc.FundPsbtRequest) returns (walletrpc.FundPsbtResponse);
* @generated from protobuf rpc: FundPsbt
*/
fundPsbt(input: FundPsbtRequest, options?: RpcOptions): UnaryCall<FundPsbtRequest, FundPsbtResponse> {
const method = this.methods[25], opt = this._transport.mergeOptions(options);
@ -929,7 +929,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* input/output/fee value validation, PSBT finalization). Any input that is
* incomplete will be skipped.
*
* @generated from protobuf rpc: SignPsbt(walletrpc.SignPsbtRequest) returns (walletrpc.SignPsbtResponse);
* @generated from protobuf rpc: SignPsbt
*/
signPsbt(input: SignPsbtRequest, options?: RpcOptions): UnaryCall<SignPsbtRequest, SignPsbtResponse> {
const method = this.methods[26], opt = this._transport.mergeOptions(options);
@ -949,7 +949,7 @@ export class WalletKitClient implements IWalletKitClient, ServiceInfo {
* caller's responsibility to either publish the transaction on success or
* unlock/release any locked UTXOs in case of an error in this method.
*
* @generated from protobuf rpc: FinalizePsbt(walletrpc.FinalizePsbtRequest) returns (walletrpc.FinalizePsbtResponse);
* @generated from protobuf rpc: FinalizePsbt
*/
finalizePsbt(input: FinalizePsbtRequest, options?: RpcOptions): UnaryCall<FinalizePsbtRequest, FinalizePsbtResponse> {
const method = this.methods[27], opt = this._transport.mergeOptions(options);

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.8.1
// @generated by protobuf-ts 2.11.1
// @generated from protobuf file "walletunlocker.proto" (package "lnrpc", syntax proto3)
// tslint:disable
import type { RpcTransport } from "@protobuf-ts/runtime-rpc";
@ -50,7 +50,7 @@ export interface IWalletUnlockerClient {
* method should be used to commit the newly generated seed, and create the
* wallet.
*
* @generated from protobuf rpc: GenSeed(lnrpc.GenSeedRequest) returns (lnrpc.GenSeedResponse);
* @generated from protobuf rpc: GenSeed
*/
genSeed(input: GenSeedRequest, options?: RpcOptions): UnaryCall<GenSeedRequest, GenSeedResponse>;
/**
@ -68,7 +68,7 @@ export interface IWalletUnlockerClient {
* seed, then present it to the user. Once it has been verified by the user,
* the seed can be fed into this RPC in order to commit the new wallet.
*
* @generated from protobuf rpc: InitWallet(lnrpc.InitWalletRequest) returns (lnrpc.InitWalletResponse);
* @generated from protobuf rpc: InitWallet
*/
initWallet(input: InitWalletRequest, options?: RpcOptions): UnaryCall<InitWalletRequest, InitWalletResponse>;
/**
@ -76,7 +76,7 @@ export interface IWalletUnlockerClient {
* UnlockWallet is used at startup of lnd to provide a password to unlock
* the wallet database.
*
* @generated from protobuf rpc: UnlockWallet(lnrpc.UnlockWalletRequest) returns (lnrpc.UnlockWalletResponse);
* @generated from protobuf rpc: UnlockWallet
*/
unlockWallet(input: UnlockWalletRequest, options?: RpcOptions): UnaryCall<UnlockWalletRequest, UnlockWalletResponse>;
/**
@ -84,7 +84,7 @@ export interface IWalletUnlockerClient {
* ChangePassword changes the password of the encrypted wallet. This will
* automatically unlock the wallet database if successful.
*
* @generated from protobuf rpc: ChangePassword(lnrpc.ChangePasswordRequest) returns (lnrpc.ChangePasswordResponse);
* @generated from protobuf rpc: ChangePassword
*/
changePassword(input: ChangePasswordRequest, options?: RpcOptions): UnaryCall<ChangePasswordRequest, ChangePasswordResponse>;
}
@ -128,7 +128,7 @@ export class WalletUnlockerClient implements IWalletUnlockerClient, ServiceInfo
* method should be used to commit the newly generated seed, and create the
* wallet.
*
* @generated from protobuf rpc: GenSeed(lnrpc.GenSeedRequest) returns (lnrpc.GenSeedResponse);
* @generated from protobuf rpc: GenSeed
*/
genSeed(input: GenSeedRequest, options?: RpcOptions): UnaryCall<GenSeedRequest, GenSeedResponse> {
const method = this.methods[0], opt = this._transport.mergeOptions(options);
@ -149,7 +149,7 @@ export class WalletUnlockerClient implements IWalletUnlockerClient, ServiceInfo
* seed, then present it to the user. Once it has been verified by the user,
* the seed can be fed into this RPC in order to commit the new wallet.
*
* @generated from protobuf rpc: InitWallet(lnrpc.InitWalletRequest) returns (lnrpc.InitWalletResponse);
* @generated from protobuf rpc: InitWallet
*/
initWallet(input: InitWalletRequest, options?: RpcOptions): UnaryCall<InitWalletRequest, InitWalletResponse> {
const method = this.methods[1], opt = this._transport.mergeOptions(options);
@ -160,7 +160,7 @@ export class WalletUnlockerClient implements IWalletUnlockerClient, ServiceInfo
* UnlockWallet is used at startup of lnd to provide a password to unlock
* the wallet database.
*
* @generated from protobuf rpc: UnlockWallet(lnrpc.UnlockWalletRequest) returns (lnrpc.UnlockWalletResponse);
* @generated from protobuf rpc: UnlockWallet
*/
unlockWallet(input: UnlockWalletRequest, options?: RpcOptions): UnaryCall<UnlockWalletRequest, UnlockWalletResponse> {
const method = this.methods[2], opt = this._transport.mergeOptions(options);
@ -171,7 +171,7 @@ export class WalletUnlockerClient implements IWalletUnlockerClient, ServiceInfo
* ChangePassword changes the password of the encrypted wallet. This will
* automatically unlock the wallet database if successful.
*
* @generated from protobuf rpc: ChangePassword(lnrpc.ChangePasswordRequest) returns (lnrpc.ChangePasswordResponse);
* @generated from protobuf rpc: ChangePassword
*/
changePassword(input: ChangePasswordRequest, options?: RpcOptions): UnaryCall<ChangePasswordRequest, ChangePasswordResponse> {
const method = this.methods[3], opt = this._transport.mergeOptions(options);

View file

@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.8.1
// @generated by protobuf-ts 2.11.1
// @generated from protobuf file "walletunlocker.proto" (package "lnrpc", syntax proto3)
// tslint:disable
import { ServiceType } from "@protobuf-ts/runtime-rpc";
@ -10,7 +10,6 @@ import type { IBinaryReader } from "@protobuf-ts/runtime";
import { UnknownFieldHandler } from "@protobuf-ts/runtime";
import type { PartialMessage } from "@protobuf-ts/runtime";
import { reflectionMergePartial } from "@protobuf-ts/runtime";
import { MESSAGE_TYPE } from "@protobuf-ts/runtime";
import { MessageType } from "@protobuf-ts/runtime";
import { ChanBackupSnapshot } from "./lightning.js";
/**
@ -23,7 +22,7 @@ export interface GenSeedRequest {
* to encrypt the generated aezeed cipher seed. When using REST, this field
* must be encoded as base64.
*
* @generated from protobuf field: bytes aezeed_passphrase = 1;
* @generated from protobuf field: bytes aezeed_passphrase = 1
*/
aezeedPassphrase: Uint8Array;
/**
@ -32,7 +31,7 @@ export interface GenSeedRequest {
* specified, then a fresh set of randomness will be used to create the seed.
* When using REST, this field must be encoded as base64.
*
* @generated from protobuf field: bytes seed_entropy = 2;
* @generated from protobuf field: bytes seed_entropy = 2
*/
seedEntropy: Uint8Array;
}
@ -48,7 +47,7 @@ export interface GenSeedResponse {
* Otherwise, then the daemon will attempt to recover the wallet state linked
* to this cipher seed.
*
* @generated from protobuf field: repeated string cipher_seed_mnemonic = 1;
* @generated from protobuf field: repeated string cipher_seed_mnemonic = 1
*/
cipherSeedMnemonic: string[];
/**
@ -56,7 +55,7 @@ export interface GenSeedResponse {
* enciphered_seed are the raw aezeed cipher seed bytes. This is the raw
* cipher text before run through our mnemonic encoding scheme.
*
* @generated from protobuf field: bytes enciphered_seed = 2;
* @generated from protobuf field: bytes enciphered_seed = 2
*/
encipheredSeed: Uint8Array;
}
@ -71,7 +70,7 @@ export interface InitWalletRequest {
* password is required to unlock the daemon. When using REST, this field
* must be encoded as base64.
*
* @generated from protobuf field: bytes wallet_password = 1;
* @generated from protobuf field: bytes wallet_password = 1
*/
walletPassword: Uint8Array;
/**
@ -80,7 +79,7 @@ export interface InitWalletRequest {
* cipher seed obtained by the user. This may have been generated by the
* GenSeed method, or be an existing seed.
*
* @generated from protobuf field: repeated string cipher_seed_mnemonic = 2;
* @generated from protobuf field: repeated string cipher_seed_mnemonic = 2
*/
cipherSeedMnemonic: string[];
/**
@ -89,7 +88,7 @@ export interface InitWalletRequest {
* to encrypt the generated aezeed cipher seed. When using REST, this field
* must be encoded as base64.
*
* @generated from protobuf field: bytes aezeed_passphrase = 3;
* @generated from protobuf field: bytes aezeed_passphrase = 3
*/
aezeedPassphrase: Uint8Array;
/**
@ -100,7 +99,7 @@ export interface InitWalletRequest {
* window of zero indicates that no addresses should be recovered, such after
* the first initialization of the wallet.
*
* @generated from protobuf field: int32 recovery_window = 4;
* @generated from protobuf field: int32 recovery_window = 4
*/
recoveryWindow: number;
/**
@ -112,7 +111,7 @@ export interface InitWalletRequest {
* funds, lnd begin to carry out the data loss recovery protocol in order to
* recover the funds in each channel from a remote force closed transaction.
*
* @generated from protobuf field: lnrpc.ChanBackupSnapshot channel_backups = 5;
* @generated from protobuf field: lnrpc.ChanBackupSnapshot channel_backups = 5
*/
channelBackups?: ChanBackupSnapshot;
/**
@ -122,7 +121,7 @@ export interface InitWalletRequest {
* admin macaroon returned in the response MUST be stored by the caller of the
* RPC as otherwise all access to the daemon will be lost!
*
* @generated from protobuf field: bool stateless_init = 6;
* @generated from protobuf field: bool stateless_init = 6
*/
statelessInit: boolean;
/**
@ -140,7 +139,7 @@ export interface InitWalletRequest {
* extended_master_key_birthday_timestamp or a "safe" default value will be
* used.
*
* @generated from protobuf field: string extended_master_key = 7;
* @generated from protobuf field: string extended_master_key = 7
*/
extendedMasterKey: string;
/**
@ -153,7 +152,7 @@ export interface InitWalletRequest {
* which case lnd will start scanning from the first SegWit block (481824 on
* mainnet).
*
* @generated from protobuf field: uint64 extended_master_key_birthday_timestamp = 8;
* @generated from protobuf field: uint64 extended_master_key_birthday_timestamp = 8
*/
extendedMasterKeyBirthdayTimestamp: bigint;
/**
@ -164,7 +163,7 @@ export interface InitWalletRequest {
* any of the keys and _needs_ to be run with a remote signer that has the
* corresponding private keys and can serve signing RPC requests.
*
* @generated from protobuf field: lnrpc.WatchOnly watch_only = 9;
* @generated from protobuf field: lnrpc.WatchOnly watch_only = 9
*/
watchOnly?: WatchOnly;
/**
@ -173,7 +172,7 @@ export interface InitWalletRequest {
* provided when initializing the wallet rather than letting lnd generate one
* on its own.
*
* @generated from protobuf field: bytes macaroon_root_key = 10;
* @generated from protobuf field: bytes macaroon_root_key = 10
*/
macaroonRootKey: Uint8Array;
}
@ -189,7 +188,7 @@ export interface InitWalletResponse {
* caller. Otherwise a copy of this macaroon is also persisted on disk by the
* daemon, together with other macaroon files.
*
* @generated from protobuf field: bytes admin_macaroon = 1;
* @generated from protobuf field: bytes admin_macaroon = 1
*/
adminMacaroon: Uint8Array;
}
@ -205,7 +204,7 @@ export interface WatchOnly {
* should be left at its default value of 0 in which case lnd will start
* scanning from the first SegWit block (481824 on mainnet).
*
* @generated from protobuf field: uint64 master_key_birthday_timestamp = 1;
* @generated from protobuf field: uint64 master_key_birthday_timestamp = 1
*/
masterKeyBirthdayTimestamp: bigint;
/**
@ -215,7 +214,7 @@ export interface WatchOnly {
* required by some hardware wallets for proper identification and signing. The
* bytes must be in big-endian order.
*
* @generated from protobuf field: bytes master_key_fingerprint = 2;
* @generated from protobuf field: bytes master_key_fingerprint = 2
*/
masterKeyFingerprint: Uint8Array;
/**
@ -226,7 +225,7 @@ export interface WatchOnly {
* scope (m/1017'/<coin_type>'/<account>'), where account is the key family as
* defined in `keychain/derivation.go` (currently indices 0 to 9).
*
* @generated from protobuf field: repeated lnrpc.WatchOnlyAccount accounts = 3;
* @generated from protobuf field: repeated lnrpc.WatchOnlyAccount accounts = 3
*/
accounts: WatchOnlyAccount[];
}
@ -239,7 +238,7 @@ export interface WatchOnlyAccount {
* Purpose is the first number in the derivation path, must be either 49, 84
* or 1017.
*
* @generated from protobuf field: uint32 purpose = 1;
* @generated from protobuf field: uint32 purpose = 1
*/
purpose: number;
/**
@ -248,7 +247,7 @@ export interface WatchOnlyAccount {
* for purposes 49 and 84. It only needs to be set to 1 for purpose 1017 on
* testnet or regtest.
*
* @generated from protobuf field: uint32 coin_type = 2;
* @generated from protobuf field: uint32 coin_type = 2
*/
coinType: number;
/**
@ -259,14 +258,14 @@ export interface WatchOnlyAccount {
* one account for each of the key families defined in `keychain/derivation.go`
* (currently indices 0 to 9)
*
* @generated from protobuf field: uint32 account = 3;
* @generated from protobuf field: uint32 account = 3
*/
account: number;
/**
*
* The extended public key at depth 3 for the given account.
*
* @generated from protobuf field: string xpub = 4;
* @generated from protobuf field: string xpub = 4
*/
xpub: string;
}
@ -280,7 +279,7 @@ export interface UnlockWalletRequest {
* will be required to decrypt on-disk material that the daemon requires to
* function properly. When using REST, this field must be encoded as base64.
*
* @generated from protobuf field: bytes wallet_password = 1;
* @generated from protobuf field: bytes wallet_password = 1
*/
walletPassword: Uint8Array;
/**
@ -291,7 +290,7 @@ export interface UnlockWalletRequest {
* window of zero indicates that no addresses should be recovered, such after
* the first initialization of the wallet.
*
* @generated from protobuf field: int32 recovery_window = 2;
* @generated from protobuf field: int32 recovery_window = 2
*/
recoveryWindow: number;
/**
@ -303,7 +302,7 @@ export interface UnlockWalletRequest {
* funds, lnd begin to carry out the data loss recovery protocol in order to
* recover the funds in each channel from a remote force closed transaction.
*
* @generated from protobuf field: lnrpc.ChanBackupSnapshot channel_backups = 3;
* @generated from protobuf field: lnrpc.ChanBackupSnapshot channel_backups = 3
*/
channelBackups?: ChanBackupSnapshot;
/**
@ -311,7 +310,7 @@ export interface UnlockWalletRequest {
* stateless_init is an optional argument instructing the daemon NOT to create
* any *.macaroon files in its file system.
*
* @generated from protobuf field: bool stateless_init = 4;
* @generated from protobuf field: bool stateless_init = 4
*/
statelessInit: boolean;
}
@ -329,7 +328,7 @@ export interface ChangePasswordRequest {
* current_password should be the current valid passphrase used to unlock the
* daemon. When using REST, this field must be encoded as base64.
*
* @generated from protobuf field: bytes current_password = 1;
* @generated from protobuf field: bytes current_password = 1
*/
currentPassword: Uint8Array;
/**
@ -337,7 +336,7 @@ export interface ChangePasswordRequest {
* new_password should be the new passphrase that will be needed to unlock the
* daemon. When using REST, this field must be encoded as base64.
*
* @generated from protobuf field: bytes new_password = 2;
* @generated from protobuf field: bytes new_password = 2
*/
newPassword: Uint8Array;
/**
@ -347,7 +346,7 @@ export interface ChangePasswordRequest {
* admin macaroon returned in the response MUST be stored by the caller of the
* RPC as otherwise all access to the daemon will be lost!
*
* @generated from protobuf field: bool stateless_init = 3;
* @generated from protobuf field: bool stateless_init = 3
*/
statelessInit: boolean;
/**
@ -356,7 +355,7 @@ export interface ChangePasswordRequest {
* rotate the macaroon root key when set to true. This will invalidate all
* previously generated macaroons.
*
* @generated from protobuf field: bool new_macaroon_root_key = 4;
* @generated from protobuf field: bool new_macaroon_root_key = 4
*/
newMacaroonRootKey: boolean;
}
@ -373,7 +372,7 @@ export interface ChangePasswordResponse {
* safely by the caller. Otherwise a copy of this macaroon is also persisted on
* disk by the daemon, together with other macaroon files.
*
* @generated from protobuf field: bytes admin_macaroon = 1;
* @generated from protobuf field: bytes admin_macaroon = 1
*/
adminMacaroon: Uint8Array;
}
@ -386,8 +385,9 @@ class GenSeedRequest$Type extends MessageType<GenSeedRequest> {
]);
}
create(value?: PartialMessage<GenSeedRequest>): GenSeedRequest {
const message = { aezeedPassphrase: new Uint8Array(0), seedEntropy: new Uint8Array(0) };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.aezeedPassphrase = new Uint8Array(0);
message.seedEntropy = new Uint8Array(0);
if (value !== undefined)
reflectionMergePartial<GenSeedRequest>(this, message, value);
return message;
@ -440,8 +440,9 @@ class GenSeedResponse$Type extends MessageType<GenSeedResponse> {
]);
}
create(value?: PartialMessage<GenSeedResponse>): GenSeedResponse {
const message = { cipherSeedMnemonic: [], encipheredSeed: new Uint8Array(0) };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.cipherSeedMnemonic = [];
message.encipheredSeed = new Uint8Array(0);
if (value !== undefined)
reflectionMergePartial<GenSeedResponse>(this, message, value);
return message;
@ -502,8 +503,15 @@ class InitWalletRequest$Type extends MessageType<InitWalletRequest> {
]);
}
create(value?: PartialMessage<InitWalletRequest>): InitWalletRequest {
const message = { walletPassword: new Uint8Array(0), cipherSeedMnemonic: [], aezeedPassphrase: new Uint8Array(0), recoveryWindow: 0, statelessInit: false, extendedMasterKey: "", extendedMasterKeyBirthdayTimestamp: 0n, macaroonRootKey: new Uint8Array(0) };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.walletPassword = new Uint8Array(0);
message.cipherSeedMnemonic = [];
message.aezeedPassphrase = new Uint8Array(0);
message.recoveryWindow = 0;
message.statelessInit = false;
message.extendedMasterKey = "";
message.extendedMasterKeyBirthdayTimestamp = 0n;
message.macaroonRootKey = new Uint8Array(0);
if (value !== undefined)
reflectionMergePartial<InitWalletRequest>(this, message, value);
return message;
@ -603,8 +611,8 @@ class InitWalletResponse$Type extends MessageType<InitWalletResponse> {
]);
}
create(value?: PartialMessage<InitWalletResponse>): InitWalletResponse {
const message = { adminMacaroon: new Uint8Array(0) };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.adminMacaroon = new Uint8Array(0);
if (value !== undefined)
reflectionMergePartial<InitWalletResponse>(this, message, value);
return message;
@ -648,12 +656,14 @@ class WatchOnly$Type extends MessageType<WatchOnly> {
super("lnrpc.WatchOnly", [
{ no: 1, name: "master_key_birthday_timestamp", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ },
{ no: 2, name: "master_key_fingerprint", kind: "scalar", T: 12 /*ScalarType.BYTES*/ },
{ no: 3, name: "accounts", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => WatchOnlyAccount }
{ no: 3, name: "accounts", kind: "message", repeat: 2 /*RepeatType.UNPACKED*/, T: () => WatchOnlyAccount }
]);
}
create(value?: PartialMessage<WatchOnly>): WatchOnly {
const message = { masterKeyBirthdayTimestamp: 0n, masterKeyFingerprint: new Uint8Array(0), accounts: [] };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.masterKeyBirthdayTimestamp = 0n;
message.masterKeyFingerprint = new Uint8Array(0);
message.accounts = [];
if (value !== undefined)
reflectionMergePartial<WatchOnly>(this, message, value);
return message;
@ -714,8 +724,11 @@ class WatchOnlyAccount$Type extends MessageType<WatchOnlyAccount> {
]);
}
create(value?: PartialMessage<WatchOnlyAccount>): WatchOnlyAccount {
const message = { purpose: 0, coinType: 0, account: 0, xpub: "" };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.purpose = 0;
message.coinType = 0;
message.account = 0;
message.xpub = "";
if (value !== undefined)
reflectionMergePartial<WatchOnlyAccount>(this, message, value);
return message;
@ -782,8 +795,10 @@ class UnlockWalletRequest$Type extends MessageType<UnlockWalletRequest> {
]);
}
create(value?: PartialMessage<UnlockWalletRequest>): UnlockWalletRequest {
const message = { walletPassword: new Uint8Array(0), recoveryWindow: 0, statelessInit: false };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.walletPassword = new Uint8Array(0);
message.recoveryWindow = 0;
message.statelessInit = false;
if (value !== undefined)
reflectionMergePartial<UnlockWalletRequest>(this, message, value);
return message;
@ -845,14 +860,26 @@ class UnlockWalletResponse$Type extends MessageType<UnlockWalletResponse> {
super("lnrpc.UnlockWalletResponse", []);
}
create(value?: PartialMessage<UnlockWalletResponse>): UnlockWalletResponse {
const message = {};
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
if (value !== undefined)
reflectionMergePartial<UnlockWalletResponse>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: UnlockWalletResponse): UnlockWalletResponse {
return target ?? this.create();
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: UnlockWalletResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
let u = options.writeUnknownFields;
@ -876,8 +903,11 @@ class ChangePasswordRequest$Type extends MessageType<ChangePasswordRequest> {
]);
}
create(value?: PartialMessage<ChangePasswordRequest>): ChangePasswordRequest {
const message = { currentPassword: new Uint8Array(0), newPassword: new Uint8Array(0), statelessInit: false, newMacaroonRootKey: false };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.currentPassword = new Uint8Array(0);
message.newPassword = new Uint8Array(0);
message.statelessInit = false;
message.newMacaroonRootKey = false;
if (value !== undefined)
reflectionMergePartial<ChangePasswordRequest>(this, message, value);
return message;
@ -941,8 +971,8 @@ class ChangePasswordResponse$Type extends MessageType<ChangePasswordResponse> {
]);
}
create(value?: PartialMessage<ChangePasswordResponse>): ChangePasswordResponse {
const message = { adminMacaroon: new Uint8Array(0) };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
const message = globalThis.Object.create((this.messagePrototype!));
message.adminMacaroon = new Uint8Array(0);
if (value !== undefined)
reflectionMergePartial<ChangePasswordResponse>(this, message, value);
return message;

View file

@ -1,11 +1,29 @@
syntax = "proto3";
import "lightning.proto";
package invoicesrpc;
import "lightning.proto";
option go_package = "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc";
/*
* Comments in this file will be directly parsed into the API
* Documentation as descriptions of the associated method, message, or field.
* These descriptions should go right above the definition of the object, and
* can be in either block or // comment format.
*
* An RPC method can be matched to an lncli command by placing a line in the
* beginning of the description in exactly the following format:
* lncli: `methodname`
*
* Failure to specify the exact name of the command will cause documentation
* generation to fail.
*
* More information on how exactly the gRPC documentation is generated from
* this proto file can be found here:
* https://github.com/lightninglabs/lightning-api
*/
// Invoices is a service that can be used to create, accept, settle and cancel
// invoices.
service Invoices {
@ -17,30 +35,39 @@ service Invoices {
rpc SubscribeSingleInvoice (SubscribeSingleInvoiceRequest)
returns (stream lnrpc.Invoice);
/*
/* lncli: `cancelinvoice`
CancelInvoice cancels a currently open invoice. If the invoice is already
canceled, this call will succeed. If the invoice is already settled, it will
fail.
*/
rpc CancelInvoice (CancelInvoiceMsg) returns (CancelInvoiceResp);
/*
/* lncli: `addholdinvoice`
AddHoldInvoice creates a hold invoice. It ties the invoice to the hash
supplied in the request.
*/
rpc AddHoldInvoice (AddHoldInvoiceRequest) returns (AddHoldInvoiceResp);
/*
/* lncli: `settleinvoice`
SettleInvoice settles an accepted invoice. If the invoice is already
settled, this call will succeed.
*/
rpc SettleInvoice (SettleInvoiceMsg) returns (SettleInvoiceResp);
/*
LookupInvoiceV2 attempts to look up at invoice. An invoice can be refrenced
LookupInvoiceV2 attempts to look up at invoice. An invoice can be referenced
using either its payment hash, payment address, or set ID.
*/
rpc LookupInvoiceV2 (LookupInvoiceMsg) returns (lnrpc.Invoice);
/*
HtlcModifier is a bidirectional streaming RPC that allows a client to
intercept and modify the HTLCs that attempt to settle the given invoice. The
server will send HTLCs of invoices to the client and the client can modify
some aspects of the HTLC in order to pass the invoice acceptance tests.
*/
rpc HtlcModifier (stream HtlcModifyResponse)
returns (stream HtlcModifyRequest);
}
message CancelInvoiceMsg {
@ -84,7 +111,7 @@ message AddHoldInvoiceRequest {
*/
bytes description_hash = 4;
// Payment request expiry time in seconds. Default is 3600 (1 hour).
// Payment request expiry time in seconds. Default is 86400 (24 hours).
int64 expiry = 5;
// Fallback on-chain address.
@ -120,8 +147,9 @@ message AddHoldInvoiceResp {
uint64 add_index = 2;
/*
The payment address of the generated invoice. This value should be used
in all payments for this invoice as we require it for end to end
The payment address of the generated invoice. This is also called
the payment secret in specifications (e.g. BOLT 11). This value should
be used in all payments for this invoice as we require it for end to end
security.
*/
bytes payment_addr = 3;
@ -173,3 +201,51 @@ message LookupInvoiceMsg {
LookupModifier lookup_modifier = 4;
}
// CircuitKey is a unique identifier for an HTLC.
message CircuitKey {
// The id of the channel that the is part of this circuit.
uint64 chan_id = 1;
// The index of the incoming htlc in the incoming channel.
uint64 htlc_id = 2;
}
message HtlcModifyRequest {
// The invoice the intercepted HTLC is attempting to settle. The HTLCs in
// the invoice are only HTLCs that have already been accepted or settled,
// not including the current intercepted HTLC.
lnrpc.Invoice invoice = 1;
// The unique identifier of the HTLC of this intercepted HTLC.
CircuitKey exit_htlc_circuit_key = 2;
// The amount in milli-satoshi that the exit HTLC is attempting to pay.
uint64 exit_htlc_amt = 3;
// The absolute expiry height of the exit HTLC.
uint32 exit_htlc_expiry = 4;
// The current block height.
uint32 current_height = 5;
// The wire message custom records of the exit HTLC.
map<uint64, bytes> exit_htlc_wire_custom_records = 6;
}
message HtlcModifyResponse {
// The circuit key of the HTLC that the client wants to modify.
CircuitKey circuit_key = 1;
// The modified amount in milli-satoshi that the exit HTLC is paying. This
// value can be different from the actual on-chain HTLC amount, in case the
// HTLC carries other valuable items, as can be the case with custom channel
// types.
optional uint64 amt_paid = 2;
// This flag indicates whether the HTLCs associated with the invoices should
// be cancelled. The interceptor client may set this field if some
// unexpected behavior is encountered. Setting this will ignore the amt_paid
// field.
bool cancel_set = 3;
}

View file

@ -274,12 +274,14 @@ service Lightning {
}
/*
SendPaymentSync is the synchronous non-streaming version of SendPayment.
This RPC is intended to be consumed by clients of the REST proxy.
Additionally, this RPC expects the destination's public key and the payment
hash (if any) to be encoded as hex strings.
Deprecated, use routerrpc.SendPaymentV2. SendPaymentSync is the synchronous
non-streaming version of SendPayment. This RPC is intended to be consumed by
clients of the REST proxy. Additionally, this RPC expects the destination's
public key and the payment hash (if any) to be encoded as hex strings.
*/
rpc SendPaymentSync (SendRequest) returns (SendResponse);
rpc SendPaymentSync (SendRequest) returns (SendResponse) {
option deprecated = true;
}
/* lncli: `sendtoroute`
Deprecated, use routerrpc.SendToRouteV2. SendToRoute is a bi-directional
@ -293,10 +295,13 @@ service Lightning {
}
/*
SendToRouteSync is a synchronous version of SendToRoute. It Will block
until the payment either fails or succeeds.
Deprecated, use routerrpc.SendToRouteV2. SendToRouteSync is a synchronous
version of SendToRoute. It Will block until the payment either fails or
succeeds.
*/
rpc SendToRouteSync (SendToRouteRequest) returns (SendResponse);
rpc SendToRouteSync (SendToRouteRequest) returns (SendResponse) {
option deprecated = true;
}
/* lncli: `addinvoice`
AddInvoice attempts to add a new invoice to the invoice database. Any
@ -643,6 +648,8 @@ message SendCustomMessageRequest {
}
message SendCustomMessageResponse {
// The status of the send operation.
string status = 1;
}
message Utxo {
@ -755,11 +762,35 @@ message GetTransactionsRequest {
// An optional filter to only include transactions relevant to an account.
string account = 3;
/*
The index of a transaction that will be used in a query to determine which
transaction should be returned in the response.
*/
uint32 index_offset = 4;
/*
The maximal number of transactions returned in the response to this query.
This value should be set to 0 to return all transactions.
*/
uint32 max_transactions = 5;
}
message TransactionDetails {
// The list of transactions relevant to the wallet.
repeated Transaction transactions = 1;
/*
The index of the last item in the set of returned transactions. This can be
used to seek further, pagination style.
*/
uint64 last_index = 2;
/*
The index of the last item in the set of returned transactions. This can be
used to seek backwards, pagination style.
*/
uint64 first_index = 3;
}
message FeeLimit {
@ -1317,6 +1348,8 @@ message ConnectPeerRequest {
uint64 timeout = 3;
}
message ConnectPeerResponse {
// The status of the connect operation.
string status = 1;
}
message DisconnectPeerRequest {
@ -1324,6 +1357,8 @@ message DisconnectPeerRequest {
string pub_key = 1;
}
message DisconnectPeerResponse {
// The status of the disconnect operation.
string status = 1;
}
message HTLC {
@ -1346,6 +1381,13 @@ message HTLC {
// Index identifying the htlc on the forwarding channel.
uint64 forwarding_htlc_index = 7;
/*
Whether the HTLC is locked in. An HTLC is considered locked in when the
remote party has sent us the `revoke_and_ack` to irrevocably commit this
HTLC.
*/
bool locked_in = 8;
}
enum CommitmentType {
@ -1388,8 +1430,14 @@ enum CommitmentType {
A channel that uses musig2 for the funding output, and the new tapscript
features where relevant.
*/
// TODO(roasbeef): need script enforce mirror type for the above as well?
SIMPLE_TAPROOT = 5;
/*
Identical to the SIMPLE_TAPROOT channel type, but with extra functionality.
This channel type also commits to additional meta data in the tapscript
leaves for the scripts in a channel.
*/
SIMPLE_TAPROOT_OVERLAY = 6;
}
message ChannelConstraints {
@ -1592,6 +1640,11 @@ message Channel {
the channel's operation.
*/
string memo = 36;
/*
Custom channel data that might be populated in custom channels.
*/
bytes custom_channel_data = 37;
}
message ListChannelsRequest {
@ -1705,6 +1758,10 @@ message ChannelCloseSummary {
// The confirmed SCID for a zero-conf channel.
uint64 zero_conf_confirmed_scid = 15 [jstype = JS_STRING];
// The TLV encoded custom channel data records for this output, which might
// be set for custom channels.
bytes custom_channel_data = 16;
}
enum ResolutionType {
@ -1955,8 +2012,8 @@ message GetInfoResponse {
bool synced_to_graph = 18;
/*
Whether the current node is connected to testnet. This field is
deprecated and the network field should be used instead
Whether the current node is connected to testnet or testnet4. This field is
deprecated and the network field should be used instead.
*/
bool testnet = 10 [deprecated = true];
@ -2028,10 +2085,38 @@ message ChannelOpenUpdate {
ChannelPoint channel_point = 1;
}
message CloseOutput {
// The amount in satoshi of this close output. This amount is the final
// commitment balance of the channel and the actual amount paid out on chain
// might be smaller due to subtracted fees.
int64 amount_sat = 1;
// The pkScript of the close output.
bytes pk_script = 2;
// Whether this output is for the local or remote node.
bool is_local = 3;
// The TLV encoded custom channel data records for this output, which might
// be set for custom channels.
bytes custom_channel_data = 4;
}
message ChannelCloseUpdate {
bytes closing_txid = 1;
bool success = 2;
// The local channel close output. If the local channel balance was dust to
// begin with, this output will not be set.
CloseOutput local_close_output = 3;
// The remote channel close output. If the remote channel balance was dust
// to begin with, this output will not be set.
CloseOutput remote_close_output = 4;
// Any additional outputs that might be added for custom channel types.
repeated CloseOutput additional_outputs = 5;
}
message CloseChannelRequest {
@ -2072,9 +2157,13 @@ message CloseChannelRequest {
// NOTE: This field is only respected if we're the initiator of the channel.
uint64 max_fee_per_vbyte = 7;
// If true, then the rpc call will not block while it awaits a closing txid.
// Consequently this RPC call will not return a closing txid if this value
// is set.
// If true, then the rpc call will not block while it awaits a closing txid
// to be broadcasted to the mempool. To obtain the closing tx one has to
// listen to the stream for the particular updates. Moreover if a coop close
// is specified and this flag is set to true the coop closing flow will be
// initiated even if HTLCs are active on the channel. The channel will wait
// until all HTLCs are resolved and then start the coop closing process. The
// channel will be disabled in the meantime and will disallow any new HTLCs.
bool no_wait = 8;
}
@ -2089,9 +2178,15 @@ message CloseStatusUpdate {
message PendingUpdate {
bytes txid = 1;
uint32 output_index = 2;
int64 fee_per_vbyte = 3;
bool local_close_tx = 4;
}
message InstantUpdate {
// The number of pending HTLCs that are currently active on the channel.
// These HTLCs need to be resolved before the channel can be closed
// cooperatively.
int32 num_pending_htlcs = 1;
}
message ReadyForPsbtFunding {
@ -2709,6 +2804,11 @@ message PendingChannelsResponse {
impacts the channel's operation.
*/
string memo = 13;
/*
Custom channel data that might be populated in custom channels.
*/
bytes custom_channel_data = 34;
}
message PendingOpenChannel {
@ -2880,6 +2980,7 @@ message ChannelEventUpdate {
ChannelPoint inactive_channel = 4;
PendingUpdate pending_open_channel = 6;
ChannelPoint fully_resolved_channel = 7;
ChannelPoint channel_funding_timeout = 8;
}
enum UpdateType {
@ -2889,6 +2990,7 @@ message ChannelEventUpdate {
INACTIVE_CHANNEL = 3;
PENDING_OPEN_CHANNEL = 4;
FULLY_RESOLVED_CHANNEL = 5;
CHANNEL_FUNDING_TIMEOUT = 6;
}
UpdateType type = 5;
@ -2968,6 +3070,12 @@ message ChannelBalanceResponse {
// Sum of channels pending remote balances.
Amount pending_open_remote_balance = 8;
/*
Custom channel data that might be populated if there are custom channels
present.
*/
bytes custom_channel_data = 9;
}
message QueryRoutesRequest {
@ -3293,6 +3401,20 @@ message Route {
The total amount in millisatoshis.
*/
int64 total_amt_msat = 6;
/*
The actual on-chain amount that was sent out to the first hop. This value is
only different from the total_amt_msat field if this is a custom channel
payment and the value transported in the HTLC is different from the BTC
amount in the HTLC. If this value is zero, then this is an old payment that
didn't have this value yet and can be ignored.
*/
int64 first_hop_amount_msat = 7;
/*
Custom channel data that might be populated in custom channels.
*/
bytes custom_channel_data = 8;
}
message NodeInfoRequest {
@ -3478,6 +3600,8 @@ message NetworkInfo {
message StopRequest {
}
message StopResponse {
// The status of the stop operation.
string status = 1;
}
message GraphTopologySubscription {
@ -3922,6 +4046,11 @@ message InvoiceHTLC {
// Details relevant to AMP HTLCs, only populated if this is an AMP HTLC.
AMP amp = 11;
/*
Custom channel data that might be populated in custom channels.
*/
bytes custom_channel_data = 12;
}
// Details specific to AMP HTLCs.
@ -4162,6 +4291,12 @@ message Payment {
uint64 payment_index = 15;
PaymentFailureReason failure_reason = 16;
/*
The custom TLV records that were sent to the first hop as part of the HTLC
wire message for this payment.
*/
map<uint64, bytes> first_hop_custom_records = 17;
}
message HTLCAttempt {
@ -4291,9 +4426,13 @@ message DeleteAllPaymentsRequest {
}
message DeletePaymentResponse {
// The status of the delete operation.
string status = 1;
}
message DeleteAllPaymentsResponse {
// The status of the delete operation.
string status = 1;
}
message AbandonChannelRequest {
@ -4310,6 +4449,8 @@ message AbandonChannelRequest {
}
message AbandonChannelResponse {
// The status of the abandon operation.
string status = 1;
}
message DebugLevelRequest {
@ -4469,6 +4610,15 @@ message PolicyUpdateRequest {
// Optional inbound fee. If unset, the previously set value will be
// retained [EXPERIMENTAL].
InboundFee inbound_fee = 10;
// Under unknown circumstances a channel can exist with a missing edge in
// the graph database. This can cause an 'edge not found' error when calling
// `getchaninfo` and/or cause the default channel policy to be used during
// forwards. Setting this flag will recreate the edge if not found, allowing
// updating this channel policy and fixing the missing edge problem for this
// channel permanently. For fields not set in this command, the default
// policy will be created.
bool create_missing_edge = 11;
}
enum UpdateFailure {
@ -4562,6 +4712,14 @@ message ForwardingEvent {
// The peer alias of the outgoing channel.
string peer_alias_out = 13;
// The ID of the incoming HTLC in the payment circuit. This field is
// optional and is unset for forwarding events happened before v0.20.
optional uint64 incoming_htlc_id = 14;
// The ID of the outgoing HTLC in the payment circuit. This field is
// optional and may be unset for legacy forwarding events.
optional uint64 outgoing_htlc_id = 15;
// TODO(roasbeef): add settlement latency?
// * use FPE on the chan id?
// * also list failures?
@ -4649,12 +4807,15 @@ message RestoreChanBackupRequest {
}
}
message RestoreBackupResponse {
// The number of channels successfully restored.
uint32 num_restored = 1;
}
message ChannelBackupSubscription {
}
message VerifyChanBackupResponse {
repeated string chan_points = 1;
}
message MacaroonPermission {
@ -4977,6 +5138,22 @@ message RPCMiddlewareRequest {
intercept message.
*/
uint64 msg_id = 7;
/*
The metadata pairs that were sent along with the original gRPC request via
the golang context.Context using explicit [gRPC
metadata](https://grpc.io/docs/guides/metadata/). Context values are not
propagated via gRPC and so we send any pairs along explicitly here so that
the interceptor can access them.
*/
map<string, MetadataValues> metadata_pairs = 9;
}
message MetadataValues {
/*
The set of metadata values that correspond to the metadata key.
*/
repeated string values = 1;
}
message StreamAuth {

View file

@ -1,22 +1,43 @@
syntax = "proto3";
import "lightning.proto";
package routerrpc;
import "lightning.proto";
option go_package = "github.com/lightningnetwork/lnd/lnrpc/routerrpc";
/*
* Comments in this file will be directly parsed into the API
* Documentation as descriptions of the associated method, message, or field.
* These descriptions should go right above the definition of the object, and
* can be in either block or // comment format.
*
* An RPC method can be matched to an lncli command by placing a line in the
* beginning of the description in exactly the following format:
* lncli: `methodname`
*
* Failure to specify the exact name of the command will cause documentation
* generation to fail.
*
* More information on how exactly the gRPC documentation is generated from
* this proto file can be found here:
* https://github.com/lightninglabs/lightning-api
*/
// Router is a service that offers advanced interaction with the router
// subsystem of the daemon.
service Router {
/*
SendPaymentV2 attempts to route a payment described by the passed
PaymentRequest to the final destination. The call returns a stream of
payment updates.
payment updates. When using this RPC, make sure to set a fee limit, as the
default routing fee limit is 0 sats. Without a non-zero fee limit only
routes without fees will be attempted which often fails with
FAILURE_REASON_NO_ROUTE.
*/
rpc SendPaymentV2 (SendPaymentRequest) returns (stream lnrpc.Payment);
/*
/* lncli: `trackpayment`
TrackPaymentV2 returns an update stream for the payment identified by the
payment hash.
*/
@ -57,21 +78,21 @@ service Router {
*/
rpc SendToRouteV2 (SendToRouteRequest) returns (lnrpc.HTLCAttempt);
/*
/* lncli: `resetmc`
ResetMissionControl clears all mission control state and starts with a clean
slate.
*/
rpc ResetMissionControl (ResetMissionControlRequest)
returns (ResetMissionControlResponse);
/*
/* lncli: `querymc`
QueryMissionControl exposes the internal mission control state to callers.
It is a development feature.
*/
rpc QueryMissionControl (QueryMissionControlRequest)
returns (QueryMissionControlResponse);
/*
/* lncli: `importmc`
XImportMissionControl is an experimental API that imports the state provided
to the internal mission control's state, using all results which are more
recent than our existing values. These values will only be imported
@ -80,30 +101,36 @@ service Router {
rpc XImportMissionControl (XImportMissionControlRequest)
returns (XImportMissionControlResponse);
/*
/* lncli: `getmccfg`
GetMissionControlConfig returns mission control's current config.
*/
rpc GetMissionControlConfig (GetMissionControlConfigRequest)
returns (GetMissionControlConfigResponse);
/*
/* lncli: `setmccfg`
SetMissionControlConfig will set mission control's config, if the config
provided is valid.
*/
rpc SetMissionControlConfig (SetMissionControlConfigRequest)
returns (SetMissionControlConfigResponse);
/*
QueryProbability returns the current success probability estimate for a
given node pair and amount.
/* lncli: `queryprob`
Deprecated. QueryProbability returns the current success probability
estimate for a given node pair and amount. The call returns a zero success
probability if no channel is available or if the amount violates min/max
HTLC constraints.
*/
rpc QueryProbability (QueryProbabilityRequest)
returns (QueryProbabilityResponse);
/*
/* lncli: `buildroute`
BuildRoute builds a fully specified route based on a list of hop public
keys. It retrieves the relevant channel policies from the graph in order to
calculate the correct fees and time locks.
Note that LND will use its default final_cltv_delta if no value is supplied.
Make sure to add the correct final_cltv_delta depending on the invoice
restriction. Moreover the caller has to make sure to provide the
payment_addr if the route is paying an invoice which signaled it.
*/
rpc BuildRoute (BuildRouteRequest) returns (BuildRouteResponse);
@ -141,7 +168,7 @@ service Router {
rpc HtlcInterceptor (stream ForwardHtlcInterceptResponse)
returns (stream ForwardHtlcInterceptRequest);
/*
/* lncli: `updatechanstatus`
UpdateChanStatus attempts to manually set the state of a channel
(enabled, disabled, or auto). A manual "disable" request will cause the
channel to stay disabled until a subsequent manual request of either
@ -149,6 +176,25 @@ service Router {
*/
rpc UpdateChanStatus (UpdateChanStatusRequest)
returns (UpdateChanStatusResponse);
/*
XAddLocalChanAliases is an experimental API that creates a set of new
channel SCID alias mappings. The final total set of aliases in the manager
after the add operation is returned. This is only a locally stored alias,
and will not be communicated to the channel peer via any message. Therefore,
routing over such an alias will only work if the peer also calls this same
RPC on their end. If an alias already exists, an error is returned
*/
rpc XAddLocalChanAliases (AddAliasesRequest) returns (AddAliasesResponse);
/*
XDeleteLocalChanAliases is an experimental API that deletes a set of alias
mappings. The final total set of aliases in the manager after the delete
operation is returned. The deletion will not be communicated to the channel
peer via any message.
*/
rpc XDeleteLocalChanAliases (DeleteAliasesRequest)
returns (DeleteAliasesResponse);
}
message SendPaymentRequest {
@ -162,13 +208,6 @@ message SendPaymentRequest {
*/
int64 amt = 2;
/*
Number of millisatoshis to send.
The fields amt and amt_msat are mutually exclusive.
*/
int64 amt_msat = 12;
// The hash to use within the payment's HTLC
bytes payment_hash = 3;
@ -178,9 +217,6 @@ message SendPaymentRequest {
*/
int32 final_cltv_delta = 4;
// An optional payment addr to be included within the last hop of the route.
bytes payment_addr = 20;
/*
A bare-bones invoice for a payment within the Lightning Network. With the
details of the invoice, the sender has all the data necessary to send a
@ -191,10 +227,11 @@ message SendPaymentRequest {
string payment_request = 5;
/*
An upper limit on the amount of time we should spend when attempting to
fulfill the payment. This is expressed in seconds. If we cannot make a
successful payment within this time frame, an error will be returned.
This field must be non-zero.
An optional limit, expressed in seconds, on the time to wait before
attempting the first HTLC. Once HTLCs are in flight, the payment will
not be aborted until the HTLCs are either settled or failed. If the field
is not set or is explicitly set to zero, the default value of 60 seconds
will be applied.
*/
int32 timeout_seconds = 6;
@ -208,17 +245,6 @@ message SendPaymentRequest {
*/
int64 fee_limit_sat = 7;
/*
The maximum number of millisatoshis that will be paid as a fee of the
payment. If this field is left to the default value of 0, only zero-fee
routes will be considered. This usually means single hop routes connecting
directly to the destination. To send the payment without a fee limit, use
max int here.
The fields fee_limit_sat and fee_limit_msat are mutually exclusive.
*/
int64 fee_limit_msat = 13;
/*
Deprecated, use outgoing_chan_ids. The channel id of the channel that must
be taken to the first hop. If zero, any channel may be used (unless
@ -227,19 +253,8 @@ message SendPaymentRequest {
uint64 outgoing_chan_id = 8 [jstype = JS_STRING, deprecated = true];
/*
The channel ids of the channels are allowed for the first hop. If empty,
any channel may be used.
*/
repeated uint64 outgoing_chan_ids = 19;
/*
The pubkey of the last hop of the route. If empty, any hop may be used.
*/
bytes last_hop_pubkey = 14;
/*
An optional maximum total time lock for the route. This should not exceed
lnd's `--max-cltv-expiry` setting. If zero, then the value of
An optional maximum total time lock for the route. This should not
exceed lnd's `--max-cltv-expiry` setting. If zero, then the value of
`--max-cltv-expiry` is enforced.
*/
int32 cltv_limit = 9;
@ -258,6 +273,29 @@ message SendPaymentRequest {
*/
map<uint64, bytes> dest_custom_records = 11;
/*
Number of millisatoshis to send.
The fields amt and amt_msat are mutually exclusive.
*/
int64 amt_msat = 12;
/*
The maximum number of millisatoshis that will be paid as a fee of the
payment. If this field is left to the default value of 0, only zero-fee
routes will be considered. This usually means single hop routes connecting
directly to the destination. To send the payment without a fee limit, use
max int here.
The fields fee_limit_sat and fee_limit_msat are mutually exclusive.
*/
int64 fee_limit_msat = 13;
/*
The pubkey of the last hop of the route. If empty, any hop may be used.
*/
bytes last_hop_pubkey = 14;
// If set, circular payments to self are permitted.
bool allow_self_payment = 15;
@ -282,6 +320,18 @@ message SendPaymentRequest {
*/
bool no_inflight_updates = 18;
/*
The channel ids of the channels are allowed for the first hop. If empty,
any channel may be used.
*/
repeated uint64 outgoing_chan_ids = 19;
/*
An optional payment addr to be included within the last hop of the route.
This is also called payment secret in specifications (e.g. BOLT 11).
*/
bytes payment_addr = 20;
/*
The largest payment split that should be attempted when making a payment if
splitting is necessary. Setting this value will effectively cause lnd to
@ -300,6 +350,24 @@ message SendPaymentRequest {
only, to 1 to optimize for reliability only or a value inbetween for a mix.
*/
double time_pref = 23;
/*
If set, the payment loop can be interrupted by manually canceling the
payment context, even before the payment timeout is reached. Note that the
payment may still succeed after cancellation, as in-flight attempts can
still settle afterwards. Canceling will only prevent further attempts from
being sent.
*/
bool cancelable = 24;
/*
An optional field that can be used to pass an arbitrary set of TLV records
to the first hop peer of this payment. This can be used to pass application
specific data during the payment attempt. Record types are required to be in
the custom range >= 65536. When using REST, the values must be encoded as
base64.
*/
map<uint64, bytes> first_hop_custom_records = 25;
}
message TrackPaymentRequest {
@ -323,14 +391,39 @@ message TrackPaymentsRequest {
message RouteFeeRequest {
/*
The destination once wishes to obtain a routing fee quote to.
The destination one wishes to obtain a routing fee quote to. If set, this
parameter requires the amt_sat parameter also to be set. This parameter
combination triggers a graph based routing fee estimation as opposed to a
payment probe based estimate in case a payment request is provided. The
graph based estimation is an algorithm that is executed on the in memory
graph. Hence its runtime is significantly shorter than a payment probe
estimation that sends out actual payments to the network.
*/
bytes dest = 1;
/*
The amount one wishes to send to the target destination.
The amount one wishes to send to the target destination. It is only to be
used in combination with the dest parameter.
*/
int64 amt_sat = 2;
/*
A payment request of the target node that the route fee request is intended
for. Its parameters are input to probe payments that estimate routing fees.
The timeout parameter can be specified to set a maximum time on the probing
attempt. Cannot be used in combination with dest and amt_sat.
*/
string payment_request = 3;
/*
A user preference of how long a probe payment should maximally be allowed to
take, denoted in seconds. The probing payment loop is aborted if this
timeout is reached. Note that the probing process itself can take longer
than the timeout if the HTLC becomes delayed or stuck. Canceling the context
of this call will not cancel the payment loop, the duration is only
controlled by the timeout parameter.
*/
uint32 timeout = 4;
}
message RouteFeeResponse {
@ -346,6 +439,12 @@ message RouteFeeResponse {
value.
*/
int64 time_lock_delay = 2;
/*
An indication whether a probing payment succeeded or whether and why it
failed. FAILURE_REASON_NONE indicates success.
*/
lnrpc.PaymentFailureReason failure_reason = 5;
}
message SendToRouteRequest {
@ -362,6 +461,15 @@ message SendToRouteRequest {
routes, incorrect payment details, or insufficient funds.
*/
bool skip_temp_err = 3;
/*
An optional field that can be used to pass an arbitrary set of TLV records
to the first hop peer of this payment. This can be used to pass application
specific data during the payment attempt. Record types are required to be in
the custom range >= 65536. When using REST, the values must be encoded as
base64.
*/
map<uint64, bytes> first_hop_custom_records = 4;
}
message SendToRouteResponse {
@ -465,6 +573,93 @@ message SetMissionControlConfigResponse {
}
message MissionControlConfig {
/*
Deprecated, use AprioriParameters. The amount of time mission control will
take to restore a penalized node or channel back to 50% success probability,
expressed in seconds. Setting this value to a higher value will penalize
failures for longer, making mission control less likely to route through
nodes and channels that we have previously recorded failures for.
*/
uint64 half_life_seconds = 1 [deprecated = true];
/*
Deprecated, use AprioriParameters. The probability of success mission
control should assign to hop in a route where it has no other information
available. Higher values will make mission control more willing to try hops
that we have no information about, lower values will discourage trying these
hops.
*/
float hop_probability = 2 [deprecated = true];
/*
Deprecated, use AprioriParameters. The importance that mission control
should place on historical results, expressed as a value in [0;1]. Setting
this value to 1 will ignore all historical payments and just use the hop
probability to assess the probability of success for each hop. A zero value
ignores hop probability completely and relies entirely on historical
results, unless none are available.
*/
float weight = 3 [deprecated = true];
/*
The maximum number of payment results that mission control will store.
*/
uint32 maximum_payment_results = 4;
/*
The minimum time that must have passed since the previously recorded failure
before we raise the failure amount.
*/
uint64 minimum_failure_relax_interval = 5;
enum ProbabilityModel {
APRIORI = 0;
BIMODAL = 1;
}
/*
ProbabilityModel defines which probability estimator should be used in
pathfinding. Note that the bimodal estimator is experimental.
*/
ProbabilityModel model = 6;
/*
EstimatorConfig is populated dependent on the estimator type.
*/
oneof EstimatorConfig {
AprioriParameters apriori = 7;
BimodalParameters bimodal = 8;
}
}
message BimodalParameters {
/*
NodeWeight defines how strongly other previous forwardings on channels of a
router should be taken into account when computing a channel's probability
to route. The allowed values are in the range [0, 1], where a value of 0
means that only direct information about a channel is taken into account.
*/
double node_weight = 1;
/*
ScaleMsat describes the scale over which channels statistically have some
liquidity left. The value determines how quickly the bimodal distribution
drops off from the edges of a channel. A larger value (compared to typical
channel capacities) means that the drop off is slow and that channel
balances are distributed more uniformly. A small value leads to the
assumption of very unbalanced channels.
*/
uint64 scale_msat = 2;
/*
DecayTime describes the information decay of knowledge about previous
successes and failures in channels. The smaller the decay time, the quicker
we forget about past forwardings.
*/
uint64 decay_time = 3;
}
message AprioriParameters {
/*
The amount of time mission control will take to restore a penalized node
or channel back to 50% success probability, expressed in seconds. Setting
@ -480,7 +675,7 @@ message MissionControlConfig {
control more willing to try hops that we have no information about, lower
values will discourage trying these hops.
*/
float hop_probability = 2;
double hop_probability = 2;
/*
The importance that mission control should place on historical results,
@ -490,18 +685,15 @@ message MissionControlConfig {
completely and relies entirely on historical results, unless none are
available.
*/
float weight = 3;
double weight = 3;
/*
The maximum number of payment results that mission control will store.
The fraction of a channel's capacity that we consider to have liquidity. For
amounts that come close to or exceed the fraction, an additional penalty is
applied. A value of 1.0 disables the capacity factor. Allowed values are in
[0.75, 1.0].
*/
uint32 maximum_payment_results = 4;
/*
The minimum time that must have passed since the previously recorded failure
before we raise the failure amount.
*/
uint64 minimum_failure_relax_interval = 5;
double capacity_fraction = 4;
}
message QueryProbabilityRequest {
@ -548,8 +740,20 @@ message BuildRouteRequest {
*/
repeated bytes hop_pubkeys = 4;
// An optional payment addr to be included within the last hop of the route.
/*
An optional payment addr to be included within the last hop of the route.
This is also called payment secret in specifications (e.g. BOLT 11).
*/
bytes payment_addr = 5;
/*
An optional field that can be used to pass an arbitrary set of TLV records
to the first hop peer of this payment. This can be used to pass application
specific data during the payment attempt. Record types are required to be in
the custom range >= 65536. When using REST, the values must be encoded as
base64.
*/
map<uint64, bytes> first_hop_custom_records = 6;
}
message BuildRouteResponse {
@ -806,12 +1010,17 @@ message ForwardHtlcInterceptRequest {
// The block height at which this htlc will be auto-failed to prevent the
// channel from force-closing.
int32 auto_fail_height = 10;
// The custom records of the peer's incoming p2p wire message.
map<uint64, bytes> in_wire_custom_records = 11;
}
/**
ForwardHtlcInterceptResponse enables the caller to resolve a previously hold
forward. The caller can choose either to:
- `Resume`: Execute the default behavior (usually forward).
- `ResumeModified`: Execute the default behavior (usually forward) with HTLC
field modifications.
- `Reject`: Fail the htlc backwards.
- `Settle`: Settle this htlc with a given preimage.
*/
@ -842,12 +1051,40 @@ message ForwardHtlcInterceptResponse {
// For backwards-compatibility reasons, TEMPORARY_CHANNEL_FAILURE is the
// default value for this field.
lnrpc.Failure.FailureCode failure_code = 5;
// The amount that was set on the p2p wire message of the incoming HTLC.
// This field is ignored if the action is not RESUME_MODIFIED or the amount
// is zero.
uint64 in_amount_msat = 6;
// The amount to set on the p2p wire message of the resumed HTLC. This field
// is ignored if the action is not RESUME_MODIFIED or the amount is zero.
uint64 out_amount_msat = 7;
// Any custom records that should be set on the p2p wire message message of
// the resumed HTLC. This field is ignored if the action is not
// RESUME_MODIFIED.
//
// This map will merge with the existing set of custom records (if any),
// replacing any conflicting types. Note that there currently is no support
// for deleting existing custom records (they can only be replaced).
map<uint64, bytes> out_wire_custom_records = 8;
}
enum ResolveHoldForwardAction {
// SETTLE is an action that is used to settle an HTLC instead of forwarding
// it.
SETTLE = 0;
// FAIL is an action that is used to fail an HTLC backwards.
FAIL = 1;
// RESUME is an action that is used to resume a forward HTLC.
RESUME = 2;
// RESUME_MODIFIED is an action that is used to resume a hold forward HTLC
// with modifications specified during interception.
RESUME_MODIFIED = 3;
}
message UpdateChanStatusRequest {
@ -864,3 +1101,19 @@ enum ChanStatusAction {
message UpdateChanStatusResponse {
}
message AddAliasesRequest {
repeated lnrpc.AliasMap alias_maps = 1;
}
message AddAliasesResponse {
repeated lnrpc.AliasMap alias_maps = 1;
}
message DeleteAliasesRequest {
repeated lnrpc.AliasMap alias_maps = 1;
}
message DeleteAliasesResponse {
repeated lnrpc.AliasMap alias_maps = 1;
}

View file

@ -1172,6 +1172,12 @@ message PendingSweep {
The deadline height used for this output when perform fee bumping.
*/
uint32 deadline_height = 14;
/*
The block height which the input's locktime will expire at. Zero if the
input has no locktime.
*/
uint32 maturity_height = 15;
}
message PendingSweepsRequest {
@ -1188,9 +1194,8 @@ message BumpFeeRequest {
// The input we're attempting to bump the fee of.
lnrpc.OutPoint outpoint = 1;
// Optional. The deadline in number of blocks that the input should be spent
// within. When not set, for new inputs, the default value (1008) is used;
// for existing inputs, their current values will be retained.
// Optional. The conf target the underlying fee estimator will use to
// estimate the starting fee rate for the fee function.
uint32 target_conf = 2;
/*
@ -1217,7 +1222,7 @@ message BumpFeeRequest {
/*
Optional. Whether this input will be swept immediately. When set to true,
the sweeper will sweep this input without waiting for the next batch.
the sweeper will sweep this input without waiting for the next block.
*/
bool immediate = 6;
@ -1230,6 +1235,12 @@ message BumpFeeRequest {
retained.
*/
uint64 budget = 7;
// Optional. The deadline delta in number of blocks that the output
// should be spent within. This translates internally to the width of the
// fee function that the sweeper will use to bump the fee rate. When the
// deadline is reached, ALL the budget will be spent as fees.
uint32 deadline_delta = 8;
}
message BumpFeeResponse {
@ -1243,7 +1254,8 @@ message BumpForceCloseFeeRequest {
lnrpc.ChannelPoint chan_point = 1;
// Optional. The deadline delta in number of blocks that the anchor output
// should be spent within to bump the closing transaction.
// should be spent within to bump the closing transaction. When the
// deadline is reached, ALL the budget will be spent as fees
uint32 deadline_delta = 2;
/*
@ -1270,6 +1282,10 @@ message BumpForceCloseFeeRequest {
transaction of the force closed channel otherwise the fee bumping will fail.
*/
uint64 budget = 5;
// Optional. The conf target the underlying fee estimator will use to
// estimate the starting fee rate for the fee function.
uint32 target_conf = 6;
}
message BumpForceCloseFeeResponse {
@ -1426,6 +1442,16 @@ message FundPsbtRequest {
// The max fee to total output amount ratio that this psbt should adhere to.
double max_fee_ratio = 12;
// The custom lock ID to use for the inputs in the funded PSBT. The value
// if set must be exactly 32 bytes long. If empty, the default lock ID will
// be used.
bytes custom_lock_id = 13;
// If set, then the inputs in the funded PSBT will be locked for the
// specified duration. The lock duration is specified in seconds. If not
// set, the default lock duration will be used.
uint64 lock_expiration_seconds = 14;
}
message FundPsbtResponse {
/*

View file

@ -926,6 +926,10 @@ message InvoiceSwapOperation {
optional UserOperation operation_payment = 2;
optional string failure_reason = 3;
optional int64 completed_at_unix = 6;
optional string refund_address = 7;
optional int64 refund_at_unix = 8;
optional string refund_tx_id = 9;
}
message InvoiceSwapsList {

731
src/extensions/README.md Normal file
View file

@ -0,0 +1,731 @@
# Lightning.Pub Extension System
A modular extension system that allows third-party functionality to be added to Lightning.Pub without modifying core code.
## Table of Contents
- [Overview](#overview)
- [Architecture](#architecture)
- [Creating an Extension](#creating-an-extension)
- [Extension Lifecycle](#extension-lifecycle)
- [ExtensionContext API](#extensioncontext-api)
- [Database Isolation](#database-isolation)
- [RPC Methods](#rpc-methods)
- [HTTP Routes](#http-routes)
- [Event Handling](#event-handling)
- [Configuration](#configuration)
- [Examples](#examples)
---
## Overview
The extension system provides:
- **Modularity**: Extensions are self-contained modules with their own code and data
- **Isolation**: Each extension gets its own SQLite database
- **Integration**: Extensions can register RPC methods, handle events, and interact with Lightning.Pub's payment and Nostr systems
- **Lifecycle Management**: Automatic discovery, loading, and graceful shutdown
### Built-in Extensions
| Extension | Description |
|-----------|-------------|
| `marketplace` | NIP-15 Nostr marketplace for selling products via Lightning |
| `withdraw` | LNURL-withdraw (LUD-03) for vouchers, faucets, and gifts |
---
## Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Lightning.Pub │
├─────────────────────────────────────────────────────────────────┤
│ Extension Loader │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Extension A │ │ Extension B │ │ Extension C │ ... │
│ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │ │
│ │ │Context│ │ │ │Context│ │ │ │Context│ │ │
│ │ └───────┘ │ │ └───────┘ │ │ └───────┘ │ │
│ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │ │
│ │ │ DB │ │ │ │ DB │ │ │ │ DB │ │ │
│ │ └───────┘ │ │ └───────┘ │ │ └───────┘ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Payment Manager │ Nostr Transport │ Application Manager │
└─────────────────────────────────────────────────────────────────┘
```
### Key Components
| Component | File | Description |
|-----------|------|-------------|
| `ExtensionLoader` | `loader.ts` | Discovers, loads, and manages extensions |
| `ExtensionContext` | `context.ts` | Bridge between extensions and Lightning.Pub |
| `ExtensionDatabase` | `database.ts` | Isolated SQLite database per extension |
---
## Creating an Extension
### Directory Structure
```
src/extensions/
└── my-extension/
├── index.ts # Main entry point (required)
├── types.ts # TypeScript interfaces
├── migrations.ts # Database migrations
└── managers/ # Business logic
└── myManager.ts
```
### Minimal Extension
```typescript
// src/extensions/my-extension/index.ts
import { Extension, ExtensionInfo, ExtensionContext, ExtensionDatabase } from '../types.js'
export default class MyExtension implements Extension {
readonly info: ExtensionInfo = {
id: 'my-extension', // Must match directory name
name: 'My Extension',
version: '1.0.0',
description: 'Does something useful',
author: 'Your Name',
minPubVersion: '1.0.0' // Minimum Lightning.Pub version
}
async initialize(ctx: ExtensionContext, db: ExtensionDatabase): Promise<void> {
// Run migrations
await db.execute(`
CREATE TABLE IF NOT EXISTS my_table (
id TEXT PRIMARY KEY,
data TEXT
)
`)
// Register RPC methods
ctx.registerMethod('my-extension.doSomething', async (req, appId) => {
return { result: 'done' }
})
ctx.log('info', 'Extension initialized')
}
async shutdown(): Promise<void> {
// Cleanup resources
}
}
```
### Extension Interface
```typescript
interface Extension {
// Required: Extension metadata
readonly info: ExtensionInfo
// Required: Called once when extension is loaded
initialize(ctx: ExtensionContext, db: ExtensionDatabase): Promise<void>
// Optional: Called when Lightning.Pub shuts down
shutdown?(): Promise<void>
// Optional: Health check for monitoring
healthCheck?(): Promise<boolean>
}
interface ExtensionInfo {
id: string // Unique identifier (lowercase, no spaces)
name: string // Display name
version: string // Semver version
description: string // Short description
author: string // Author name
minPubVersion?: string // Minimum Lightning.Pub version
dependencies?: string[] // Other extension IDs required
}
```
---
## Extension Lifecycle
```
┌──────────────┐
│ Discover │ Scan extensions directory for index.ts files
└──────┬───────┘
┌──────────────┐
│ Load │ Import module, instantiate class
└──────┬───────┘
┌──────────────┐
│ Initialize │ Create database, call initialize()
└──────┬───────┘
┌──────────────┐
│ Ready │ Extension is active, handling requests
└──────┬───────┘
▼ (on shutdown)
┌──────────────┐
│ Shutdown │ Call shutdown(), close database
└──────────────┘
```
### States
| State | Description |
|-------|-------------|
| `loading` | Extension is being loaded |
| `ready` | Extension is active and healthy |
| `error` | Initialization failed |
| `stopped` | Extension has been shut down |
---
## ExtensionContext API
The `ExtensionContext` is passed to your extension during initialization. It provides access to Lightning.Pub functionality.
### Application Management
```typescript
// Get information about an application
const app = await ctx.getApplication(applicationId)
// Returns: { id, name, nostr_public, balance_sats } | null
```
### Payment Operations
```typescript
// Create a Lightning invoice
const invoice = await ctx.createInvoice(amountSats, {
memo: 'Payment for service',
expiry: 3600, // seconds
metadata: { order_id: '123' } // Returned in payment callback
})
// Returns: { id, paymentRequest, paymentHash, expiry }
// Pay a Lightning invoice
const result = await ctx.payInvoice(applicationId, bolt11Invoice, maxFeeSats)
// Returns: { paymentHash, feeSats }
```
### Nostr Operations
```typescript
// Send encrypted DM (NIP-44)
const eventId = await ctx.sendEncryptedDM(applicationId, recipientPubkey, content)
// Publish a Nostr event (signed by application's key)
const eventId = await ctx.publishNostrEvent({
kind: 30017,
pubkey: appPubkey,
created_at: Math.floor(Date.now() / 1000),
tags: [['d', 'identifier']],
content: JSON.stringify(data)
})
```
### RPC Method Registration
```typescript
// Register a method that can be called via RPC
ctx.registerMethod('my-extension.methodName', async (request, applicationId, userPubkey?) => {
// request: The RPC request payload
// applicationId: The calling application's ID
// userPubkey: The user's Nostr pubkey (if authenticated)
return { result: 'success' }
})
```
### Event Subscriptions
```typescript
// Subscribe to payment received events
ctx.onPaymentReceived(async (payment) => {
// payment: { invoiceId, paymentHash, amountSats, metadata }
if (payment.metadata?.extension === 'my-extension') {
// Handle payment for this extension
}
})
// Subscribe to incoming Nostr events
ctx.onNostrEvent(async (event, applicationId) => {
// event: { id, pubkey, kind, tags, content, created_at }
// applicationId: The application this event is for
if (event.kind === 4) { // DM
// Handle incoming message
}
})
```
### Logging
```typescript
ctx.log('debug', 'Detailed debugging info')
ctx.log('info', 'Normal operation info')
ctx.log('warn', 'Warning message')
ctx.log('error', 'Error occurred', errorObject)
```
---
## Database Isolation
Each extension gets its own SQLite database file at:
```
{databaseDir}/{extension-id}.db
```
### Database Interface
```typescript
interface ExtensionDatabase {
// Execute write queries (INSERT, UPDATE, DELETE, CREATE)
execute(sql: string, params?: any[]): Promise<{ changes?: number; lastId?: number }>
// Execute read queries (SELECT)
query<T>(sql: string, params?: any[]): Promise<T[]>
// Run multiple statements in a transaction
transaction<T>(fn: () => Promise<T>): Promise<T>
}
```
### Migration Pattern
```typescript
// migrations.ts
export interface Migration {
version: number
name: string
up: (db: ExtensionDatabase) => Promise<void>
}
export const migrations: Migration[] = [
{
version: 1,
name: 'create_initial_tables',
up: async (db) => {
await db.execute(`
CREATE TABLE items (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
created_at INTEGER NOT NULL
)
`)
}
},
{
version: 2,
name: 'add_status_column',
up: async (db) => {
await db.execute(`ALTER TABLE items ADD COLUMN status TEXT DEFAULT 'active'`)
}
}
]
// Run migrations in initialize()
export async function runMigrations(db: ExtensionDatabase): Promise<void> {
const result = await db.query<{ value: string }>(
`SELECT value FROM _extension_meta WHERE key = 'migration_version'`
).catch(() => [])
const currentVersion = result.length > 0 ? parseInt(result[0].value, 10) : 0
for (const migration of migrations) {
if (migration.version > currentVersion) {
console.log(`Running migration ${migration.version}: ${migration.name}`)
await migration.up(db)
await db.execute(
`INSERT INTO _extension_meta (key, value) VALUES ('migration_version', ?)
ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
[String(migration.version)]
)
}
}
}
```
---
## RPC Methods
Extensions register RPC methods that can be called by clients.
### Naming Convention
Methods should be namespaced with the extension ID:
```
{extension-id}.{methodName}
```
Examples:
- `marketplace.createStall`
- `withdraw.createLink`
### Method Handler Signature
```typescript
type RpcMethodHandler = (
request: any, // The request payload
applicationId: string, // The calling application
userPubkey?: string // The authenticated user (if any)
) => Promise<any>
```
### Example
```typescript
ctx.registerMethod('my-extension.createItem', async (req, appId, userPubkey) => {
// Validate request
if (!req.name) {
throw new Error('Name is required')
}
// Create item
const item = await this.manager.create(appId, req)
// Return response
return { item }
})
```
---
## HTTP Routes
Some extensions need HTTP endpoints (e.g., LNURL protocol). Extensions can define routes that the main application mounts.
### Defining Routes
```typescript
interface HttpRoute {
method: 'GET' | 'POST'
path: string
handler: (req: HttpRequest) => Promise<HttpResponse>
}
interface HttpRequest {
params: Record<string, string> // URL path params
query: Record<string, string> // Query string params
body?: any // POST body
headers: Record<string, string>
}
interface HttpResponse {
status: number
body: any
headers?: Record<string, string>
}
```
### Example
```typescript
class MyExtension implements Extension {
getHttpRoutes(): HttpRoute[] {
return [
{
method: 'GET',
path: '/api/v1/my-extension/:id',
handler: async (req) => {
const item = await this.getItem(req.params.id)
return {
status: 200,
body: item,
headers: { 'Content-Type': 'application/json' }
}
}
}
]
}
}
```
---
## Event Handling
### Payment Callbacks
When you create an invoice with metadata, you'll receive that metadata back in the payment callback:
```typescript
// Creating invoice with metadata
const invoice = await ctx.createInvoice(1000, {
metadata: {
extension: 'my-extension',
order_id: 'order-123'
}
})
// Handling payment
ctx.onPaymentReceived(async (payment) => {
if (payment.metadata?.extension === 'my-extension') {
const orderId = payment.metadata.order_id
await this.handlePayment(orderId, payment)
}
})
```
### Nostr Events
Subscribe to Nostr events for your application:
```typescript
ctx.onNostrEvent(async (event, applicationId) => {
// Filter by event kind
if (event.kind === 4) { // Encrypted DM
await this.handleDirectMessage(event, applicationId)
}
})
```
---
## Configuration
### Loader Configuration
```typescript
interface ExtensionLoaderConfig {
extensionsDir: string // Directory containing extensions
databaseDir: string // Directory for extension databases
enabledExtensions?: string[] // Whitelist (if set, only these load)
disabledExtensions?: string[] // Blacklist
}
```
### Usage
```typescript
import { createExtensionLoader } from './extensions'
const loader = createExtensionLoader({
extensionsDir: './src/extensions',
databaseDir: './data/extensions',
disabledExtensions: ['experimental-ext']
}, mainHandler)
await loader.loadAll()
// Call extension methods
const result = await loader.callMethod(
'marketplace.createStall',
{ name: 'My Shop', currency: 'sat', shipping_zones: [] },
applicationId,
userPubkey
)
// Dispatch events
loader.dispatchPaymentReceived(paymentData)
loader.dispatchNostrEvent(event, applicationId)
// Shutdown
await loader.shutdown()
```
---
## Examples
### Example: Simple Counter Extension
```typescript
// src/extensions/counter/index.ts
import { Extension, ExtensionInfo, ExtensionContext, ExtensionDatabase } from '../types.js'
export default class CounterExtension implements Extension {
readonly info: ExtensionInfo = {
id: 'counter',
name: 'Simple Counter',
version: '1.0.0',
description: 'A simple counter for each application',
author: 'Example'
}
private db!: ExtensionDatabase
async initialize(ctx: ExtensionContext, db: ExtensionDatabase): Promise<void> {
this.db = db
await db.execute(`
CREATE TABLE IF NOT EXISTS counters (
application_id TEXT PRIMARY KEY,
count INTEGER NOT NULL DEFAULT 0
)
`)
ctx.registerMethod('counter.increment', async (req, appId) => {
await db.execute(
`INSERT INTO counters (application_id, count) VALUES (?, 1)
ON CONFLICT(application_id) DO UPDATE SET count = count + 1`,
[appId]
)
const result = await db.query<{ count: number }>(
'SELECT count FROM counters WHERE application_id = ?',
[appId]
)
return { count: result[0]?.count || 0 }
})
ctx.registerMethod('counter.get', async (req, appId) => {
const result = await db.query<{ count: number }>(
'SELECT count FROM counters WHERE application_id = ?',
[appId]
)
return { count: result[0]?.count || 0 }
})
ctx.registerMethod('counter.reset', async (req, appId) => {
await db.execute(
'UPDATE counters SET count = 0 WHERE application_id = ?',
[appId]
)
return { count: 0 }
})
}
}
```
### Example: Payment-Triggered Extension
```typescript
// src/extensions/donations/index.ts
import { Extension, ExtensionContext, ExtensionDatabase } from '../types.js'
export default class DonationsExtension implements Extension {
readonly info = {
id: 'donations',
name: 'Donations',
version: '1.0.0',
description: 'Accept donations with thank-you messages',
author: 'Example'
}
private db!: ExtensionDatabase
private ctx!: ExtensionContext
async initialize(ctx: ExtensionContext, db: ExtensionDatabase): Promise<void> {
this.db = db
this.ctx = ctx
await db.execute(`
CREATE TABLE IF NOT EXISTS donations (
id TEXT PRIMARY KEY,
application_id TEXT NOT NULL,
amount_sats INTEGER NOT NULL,
donor_pubkey TEXT,
message TEXT,
created_at INTEGER NOT NULL
)
`)
// Create donation invoice
ctx.registerMethod('donations.createInvoice', async (req, appId) => {
const invoice = await ctx.createInvoice(req.amount_sats, {
memo: req.message || 'Donation',
metadata: {
extension: 'donations',
donor_pubkey: req.donor_pubkey,
message: req.message
}
})
return { invoice: invoice.paymentRequest }
})
// Handle successful payments
ctx.onPaymentReceived(async (payment) => {
if (payment.metadata?.extension !== 'donations') return
// Record donation
await db.execute(
`INSERT INTO donations (id, application_id, amount_sats, donor_pubkey, message, created_at)
VALUES (?, ?, ?, ?, ?, ?)`,
[
payment.paymentHash,
payment.metadata.application_id,
payment.amountSats,
payment.metadata.donor_pubkey,
payment.metadata.message,
Math.floor(Date.now() / 1000)
]
)
// Send thank-you DM if donor has pubkey
if (payment.metadata.donor_pubkey) {
await ctx.sendEncryptedDM(
payment.metadata.application_id,
payment.metadata.donor_pubkey,
`Thank you for your donation of ${payment.amountSats} sats!`
)
}
})
// List donations
ctx.registerMethod('donations.list', async (req, appId) => {
const donations = await db.query(
`SELECT * FROM donations WHERE application_id = ? ORDER BY created_at DESC LIMIT ?`,
[appId, req.limit || 50]
)
return { donations }
})
}
}
```
---
## Best Practices
1. **Namespace your methods**: Always prefix RPC methods with your extension ID
2. **Use migrations**: Never modify existing migration files; create new ones
3. **Handle errors gracefully**: Throw descriptive errors, don't return error objects
4. **Clean up in shutdown**: Close connections, cancel timers, etc.
5. **Log appropriately**: Use debug for verbose info, error for failures
6. **Validate inputs**: Check request parameters before processing
7. **Use transactions**: For multi-step database operations
8. **Document your API**: Include types and descriptions for RPC methods
---
## Troubleshooting
### Extension not loading
1. Check that directory name matches `info.id`
2. Verify `index.ts` has a default export
3. Check for TypeScript/import errors in logs
### Database errors
1. Check migration syntax
2. Verify column types match queries
3. Look for migration version conflicts
### RPC method not found
1. Verify method is registered in `initialize()`
2. Check method name includes extension prefix
3. Ensure extension status is `ready`
### Payment callbacks not firing
1. Verify `metadata.extension` matches your extension ID
2. Check that `onPaymentReceived` is registered in `initialize()`
3. Confirm invoice was created through the extension

309
src/extensions/context.ts Normal file
View file

@ -0,0 +1,309 @@
import {
ExtensionContext,
ExtensionDatabase,
ExtensionInfo,
ApplicationInfo,
CreateInvoiceOptions,
CreatedInvoice,
PaymentReceivedData,
NostrEvent,
UnsignedNostrEvent,
RpcMethodHandler,
LnurlPayInfo
} from './types.js'
/**
* Main Handler interface (from Lightning.Pub)
* This is a minimal interface - the actual MainHandler has more methods
*/
export interface MainHandlerInterface {
// Application management
applicationManager: {
getById(id: string): Promise<any>
}
// Payment operations
paymentManager: {
createInvoice(params: {
applicationId: string
amountSats: number
memo?: string
expiry?: number
metadata?: Record<string, any>
}): Promise<{
id: string
paymentRequest: string
paymentHash: string
expiry: number
}>
payInvoice(params: {
applicationId: string
paymentRequest: string
maxFeeSats?: number
}): Promise<{
paymentHash: string
feeSats: number
}>
/**
* Get LNURL-pay info for a user by their Nostr pubkey
* This enables Lightning Address (LUD-16) and zap (NIP-57) support
*/
getLnurlPayInfoByPubkey(pubkeyHex: string, options?: {
metadata?: string
description?: string
}): Promise<LnurlPayInfo>
}
// Nostr operations
sendNostrEvent(event: any): Promise<string | null>
sendEncryptedDM(applicationId: string, recipientPubkey: string, content: string): Promise<string>
}
/**
* Callback registries for extension events
*/
interface CallbackRegistries {
paymentReceived: Array<(payment: PaymentReceivedData) => Promise<void>>
nostrEvent: Array<(event: NostrEvent, applicationId: string) => Promise<void>>
}
/**
* Registered RPC method
*/
interface RegisteredMethod {
extensionId: string
handler: RpcMethodHandler
}
/**
* Extension Context Implementation
*
* Provides the interface for extensions to interact with Lightning.Pub.
* Each extension gets its own context instance.
*/
export class ExtensionContextImpl implements ExtensionContext {
private callbacks: CallbackRegistries = {
paymentReceived: [],
nostrEvent: []
}
constructor(
private extensionInfo: ExtensionInfo,
private database: ExtensionDatabase,
private mainHandler: MainHandlerInterface,
private methodRegistry: Map<string, RegisteredMethod>
) {}
/**
* Get information about an application
*/
async getApplication(applicationId: string): Promise<ApplicationInfo | null> {
try {
const app = await this.mainHandler.applicationManager.getById(applicationId)
if (!app) return null
return {
id: app.id,
name: app.name,
nostr_public: app.nostr_public,
balance_sats: app.balance || 0
}
} catch (e) {
this.log('error', `Failed to get application ${applicationId}:`, e)
return null
}
}
/**
* Create a Lightning invoice
*/
async createInvoice(amountSats: number, options: CreateInvoiceOptions = {}): Promise<CreatedInvoice> {
// Note: In practice, this needs an applicationId. Extensions typically
// get this from the RPC request context. For now, we'll need to handle
// this in the actual implementation.
throw new Error('createInvoice requires applicationId from request context')
}
/**
* Create invoice with explicit application ID
* This is the internal method used by extensions
*/
async createInvoiceForApp(
applicationId: string,
amountSats: number,
options: CreateInvoiceOptions = {}
): Promise<CreatedInvoice> {
const result = await this.mainHandler.paymentManager.createInvoice({
applicationId,
amountSats,
memo: options.memo,
expiry: options.expiry,
metadata: {
...options.metadata,
extension: this.extensionInfo.id
}
})
return {
id: result.id,
paymentRequest: result.paymentRequest,
paymentHash: result.paymentHash,
expiry: result.expiry
}
}
/**
* Pay a Lightning invoice
*/
async payInvoice(
applicationId: string,
paymentRequest: string,
maxFeeSats?: number
): Promise<{ paymentHash: string; feeSats: number }> {
return this.mainHandler.paymentManager.payInvoice({
applicationId,
paymentRequest,
maxFeeSats
})
}
/**
* Send an encrypted DM via Nostr
*/
async sendEncryptedDM(
applicationId: string,
recipientPubkey: string,
content: string
): Promise<string> {
return this.mainHandler.sendEncryptedDM(applicationId, recipientPubkey, content)
}
/**
* Publish a Nostr event
*/
async publishNostrEvent(event: UnsignedNostrEvent): Promise<string | null> {
return this.mainHandler.sendNostrEvent(event)
}
/**
* Get LNURL-pay info for a user by pubkey
* Enables Lightning Address and zap support
*/
async getLnurlPayInfo(pubkeyHex: string, options?: {
metadata?: string
description?: string
}): Promise<LnurlPayInfo> {
return this.mainHandler.paymentManager.getLnurlPayInfoByPubkey(pubkeyHex, options)
}
/**
* Subscribe to payment received callbacks
*/
onPaymentReceived(callback: (payment: PaymentReceivedData) => Promise<void>): void {
this.callbacks.paymentReceived.push(callback)
}
/**
* Subscribe to incoming Nostr events
*/
onNostrEvent(callback: (event: NostrEvent, applicationId: string) => Promise<void>): void {
this.callbacks.nostrEvent.push(callback)
}
/**
* Register an RPC method
*/
registerMethod(name: string, handler: RpcMethodHandler): void {
const fullName = name.startsWith(`${this.extensionInfo.id}.`)
? name
: `${this.extensionInfo.id}.${name}`
if (this.methodRegistry.has(fullName)) {
throw new Error(`RPC method ${fullName} already registered`)
}
this.methodRegistry.set(fullName, {
extensionId: this.extensionInfo.id,
handler
})
this.log('debug', `Registered RPC method: ${fullName}`)
}
/**
* Get the extension's database
*/
getDatabase(): ExtensionDatabase {
return this.database
}
/**
* Log a message
*/
log(level: 'debug' | 'info' | 'warn' | 'error', message: string, ...args: any[]): void {
const prefix = `[Extension:${this.extensionInfo.id}]`
switch (level) {
case 'debug':
console.debug(prefix, message, ...args)
break
case 'info':
console.info(prefix, message, ...args)
break
case 'warn':
console.warn(prefix, message, ...args)
break
case 'error':
console.error(prefix, message, ...args)
break
}
}
// ===== Internal Methods (called by ExtensionLoader) =====
/**
* Dispatch payment received event to extension callbacks
*/
async dispatchPaymentReceived(payment: PaymentReceivedData): Promise<void> {
for (const callback of this.callbacks.paymentReceived) {
try {
await callback(payment)
} catch (e) {
this.log('error', 'Error in payment callback:', e)
}
}
}
/**
* Dispatch Nostr event to extension callbacks
*/
async dispatchNostrEvent(event: NostrEvent, applicationId: string): Promise<void> {
for (const callback of this.callbacks.nostrEvent) {
try {
await callback(event, applicationId)
} catch (e) {
this.log('error', 'Error in Nostr event callback:', e)
}
}
}
/**
* Get registered callbacks for external access
*/
getCallbacks(): CallbackRegistries {
return this.callbacks
}
}
/**
* Create an extension context
*/
export function createExtensionContext(
extensionInfo: ExtensionInfo,
database: ExtensionDatabase,
mainHandler: MainHandlerInterface,
methodRegistry: Map<string, RegisteredMethod>
): ExtensionContextImpl {
return new ExtensionContextImpl(extensionInfo, database, mainHandler, methodRegistry)
}

148
src/extensions/database.ts Normal file
View file

@ -0,0 +1,148 @@
import Database from 'better-sqlite3'
import path from 'path'
import fs from 'fs'
import { ExtensionDatabase } from './types.js'
/**
* Extension Database Implementation
*
* Provides isolated SQLite database access for each extension.
* Uses better-sqlite3 for synchronous, high-performance access.
*/
export class ExtensionDatabaseImpl implements ExtensionDatabase {
private db: Database.Database
private extensionId: string
constructor(extensionId: string, databaseDir: string) {
this.extensionId = extensionId
// Ensure database directory exists
if (!fs.existsSync(databaseDir)) {
fs.mkdirSync(databaseDir, { recursive: true })
}
// Create database file for this extension
const dbPath = path.join(databaseDir, `${extensionId}.db`)
this.db = new Database(dbPath)
// Enable WAL mode for better concurrency
this.db.pragma('journal_mode = WAL')
// Enable foreign keys
this.db.pragma('foreign_keys = ON')
// Create metadata table for tracking migrations
this.db.exec(`
CREATE TABLE IF NOT EXISTS _extension_meta (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
)
`)
}
/**
* Execute a write query (INSERT, UPDATE, DELETE, CREATE, etc.)
*/
async execute(sql: string, params: any[] = []): Promise<{ changes?: number; lastId?: number }> {
try {
const stmt = this.db.prepare(sql)
const result = stmt.run(...params)
return {
changes: result.changes,
lastId: result.lastInsertRowid as number
}
} catch (e) {
console.error(`[Extension:${this.extensionId}] Database execute error:`, e)
throw e
}
}
/**
* Execute a read query (SELECT)
*/
async query<T = any>(sql: string, params: any[] = []): Promise<T[]> {
try {
const stmt = this.db.prepare(sql)
return stmt.all(...params) as T[]
} catch (e) {
console.error(`[Extension:${this.extensionId}] Database query error:`, e)
throw e
}
}
/**
* Execute multiple statements in a transaction
*/
async transaction<T>(fn: () => Promise<T>): Promise<T> {
const runTransaction = this.db.transaction(() => {
// Note: better-sqlite3 transactions are synchronous
// We wrap the async function but it executes synchronously
return fn()
})
return runTransaction() as T
}
/**
* Get a metadata value
*/
async getMeta(key: string): Promise<string | null> {
const rows = await this.query<{ value: string }>(
'SELECT value FROM _extension_meta WHERE key = ?',
[key]
)
return rows.length > 0 ? rows[0].value : null
}
/**
* Set a metadata value
*/
async setMeta(key: string, value: string): Promise<void> {
await this.execute(
`INSERT INTO _extension_meta (key, value) VALUES (?, ?)
ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
[key, value]
)
}
/**
* Get current migration version
*/
async getMigrationVersion(): Promise<number> {
const version = await this.getMeta('migration_version')
return version ? parseInt(version, 10) : 0
}
/**
* Set migration version
*/
async setMigrationVersion(version: number): Promise<void> {
await this.setMeta('migration_version', String(version))
}
/**
* Close the database connection
*/
close(): void {
this.db.close()
}
/**
* Get the underlying database for advanced operations
* (Use with caution - bypasses isolation)
*/
getUnderlyingDb(): Database.Database {
return this.db
}
}
/**
* Create an extension database instance
*/
export function createExtensionDatabase(
extensionId: string,
databaseDir: string
): ExtensionDatabaseImpl {
return new ExtensionDatabaseImpl(extensionId, databaseDir)
}

56
src/extensions/index.ts Normal file
View file

@ -0,0 +1,56 @@
/**
* Lightning.Pub Extension System
*
* This module provides the extension infrastructure for Lightning.Pub.
* Extensions can add functionality like marketplaces, subscriptions,
* tipping, and more.
*
* Usage:
*
* ```typescript
* import { createExtensionLoader, ExtensionLoaderConfig } from './extensions'
*
* const config: ExtensionLoaderConfig = {
* extensionsDir: './extensions',
* databaseDir: './data/extensions'
* }
*
* const loader = createExtensionLoader(config, mainHandler)
* await loader.loadAll()
*
* // Call extension methods
* const result = await loader.callMethod(
* 'marketplace.createStall',
* { name: 'My Shop', currency: 'sat', shipping_zones: [...] },
* applicationId
* )
* ```
*/
// Export types
export {
Extension,
ExtensionInfo,
ExtensionContext,
ExtensionDatabase,
ExtensionModule,
ExtensionConstructor,
LoadedExtension,
ExtensionLoaderConfig,
ApplicationInfo,
CreateInvoiceOptions,
CreatedInvoice,
PaymentReceivedData,
NostrEvent,
UnsignedNostrEvent,
RpcMethodHandler
} from './types.js'
// Export loader
export { ExtensionLoader, createExtensionLoader } from './loader.js'
// Export database utilities
export { ExtensionDatabaseImpl, createExtensionDatabase } from './database.js'
// Export context utilities
export { ExtensionContextImpl, createExtensionContext, MainHandlerInterface } from './context.js'

406
src/extensions/loader.ts Normal file
View file

@ -0,0 +1,406 @@
import path from 'path'
import fs from 'fs'
import {
Extension,
ExtensionInfo,
ExtensionModule,
LoadedExtension,
ExtensionLoaderConfig,
RpcMethodHandler,
PaymentReceivedData,
NostrEvent
} from './types.js'
import { ExtensionDatabaseImpl, createExtensionDatabase } from './database.js'
import { ExtensionContextImpl, createExtensionContext, MainHandlerInterface } from './context.js'
/**
* Registered RPC method entry
*/
interface RegisteredMethod {
extensionId: string
handler: RpcMethodHandler
}
/**
* Extension Loader
*
* Discovers, loads, and manages Lightning.Pub extensions.
* Provides lifecycle management and event dispatching.
*/
export class ExtensionLoader {
private config: ExtensionLoaderConfig
private mainHandler: MainHandlerInterface
private extensions: Map<string, LoadedExtension> = new Map()
private contexts: Map<string, ExtensionContextImpl> = new Map()
private methodRegistry: Map<string, RegisteredMethod> = new Map()
private initialized = false
constructor(config: ExtensionLoaderConfig, mainHandler: MainHandlerInterface) {
this.config = config
this.mainHandler = mainHandler
}
/**
* Discover and load all extensions
*/
async loadAll(): Promise<void> {
if (this.initialized) {
throw new Error('Extension loader already initialized')
}
console.log('[Extensions] Loading extensions from:', this.config.extensionsDir)
// Ensure directories exist
if (!fs.existsSync(this.config.extensionsDir)) {
console.log('[Extensions] Extensions directory does not exist, creating...')
fs.mkdirSync(this.config.extensionsDir, { recursive: true })
this.initialized = true
return
}
if (!fs.existsSync(this.config.databaseDir)) {
fs.mkdirSync(this.config.databaseDir, { recursive: true })
}
// Discover extensions
const extensionDirs = await this.discoverExtensions()
console.log(`[Extensions] Found ${extensionDirs.length} extension(s)`)
// Load extensions in dependency order
const loadOrder = await this.resolveDependencies(extensionDirs)
for (const extDir of loadOrder) {
try {
await this.loadExtension(extDir)
} catch (e) {
console.error(`[Extensions] Failed to load extension from ${extDir}:`, e)
}
}
this.initialized = true
console.log(`[Extensions] Loaded ${this.extensions.size} extension(s)`)
}
/**
* Discover extension directories
*/
private async discoverExtensions(): Promise<string[]> {
const entries = fs.readdirSync(this.config.extensionsDir, { withFileTypes: true })
const extensionDirs: string[] = []
for (const entry of entries) {
if (!entry.isDirectory()) continue
const extDir = path.join(this.config.extensionsDir, entry.name)
const indexPath = path.join(extDir, 'index.ts')
const indexJsPath = path.join(extDir, 'index.js')
// Check for index file
if (fs.existsSync(indexPath) || fs.existsSync(indexJsPath)) {
// Check enabled/disabled lists
if (this.config.disabledExtensions?.includes(entry.name)) {
console.log(`[Extensions] Skipping disabled extension: ${entry.name}`)
continue
}
if (this.config.enabledExtensions &&
!this.config.enabledExtensions.includes(entry.name)) {
console.log(`[Extensions] Skipping non-enabled extension: ${entry.name}`)
continue
}
extensionDirs.push(extDir)
}
}
return extensionDirs
}
/**
* Resolve extension dependencies and return load order
*/
private async resolveDependencies(extensionDirs: string[]): Promise<string[]> {
// For now, simple alphabetical order
// TODO: Implement proper dependency resolution with topological sort
return extensionDirs.sort()
}
/**
* Load a single extension
*/
private async loadExtension(extensionDir: string): Promise<void> {
const dirName = path.basename(extensionDir)
console.log(`[Extensions] Loading extension: ${dirName}`)
// Determine index file path
let indexPath = path.join(extensionDir, 'index.js')
if (!fs.existsSync(indexPath)) {
indexPath = path.join(extensionDir, 'index.ts')
}
// Dynamic import
const moduleUrl = `file://${indexPath}`
const module = await import(moduleUrl) as ExtensionModule
if (!module.default) {
throw new Error(`Extension ${dirName} has no default export`)
}
// Instantiate extension
const ExtensionClass = module.default
const instance = new ExtensionClass() as Extension
if (!instance.info) {
throw new Error(`Extension ${dirName} has no info property`)
}
const info = instance.info
// Validate extension ID matches directory name
if (info.id !== dirName) {
console.warn(
`[Extensions] Extension ID '${info.id}' doesn't match directory '${dirName}'`
)
}
// Check for duplicate
if (this.extensions.has(info.id)) {
throw new Error(`Extension ${info.id} already loaded`)
}
// Create isolated database
const database = createExtensionDatabase(info.id, this.config.databaseDir)
// Create context
const context = createExtensionContext(
info,
database,
this.mainHandler,
this.methodRegistry
)
// Track as loading
const loaded: LoadedExtension = {
info,
instance,
database,
status: 'loading',
loadedAt: Date.now()
}
this.extensions.set(info.id, loaded)
this.contexts.set(info.id, context)
try {
// Initialize extension
await instance.initialize(context, database)
loaded.status = 'ready'
console.log(`[Extensions] Extension ${info.id} v${info.version} loaded successfully`)
} catch (e) {
loaded.status = 'error'
loaded.error = e as Error
console.error(`[Extensions] Extension ${info.id} initialization failed:`, e)
throw e
}
}
/**
* Unload a specific extension
*/
async unloadExtension(extensionId: string): Promise<void> {
const loaded = this.extensions.get(extensionId)
if (!loaded) {
throw new Error(`Extension ${extensionId} not found`)
}
console.log(`[Extensions] Unloading extension: ${extensionId}`)
try {
// Call shutdown if available
if (loaded.instance.shutdown) {
await loaded.instance.shutdown()
}
loaded.status = 'stopped'
} catch (e) {
console.error(`[Extensions] Error during ${extensionId} shutdown:`, e)
}
// Close database
if (loaded.database instanceof ExtensionDatabaseImpl) {
loaded.database.close()
}
// Remove registered methods
for (const [name, method] of this.methodRegistry.entries()) {
if (method.extensionId === extensionId) {
this.methodRegistry.delete(name)
}
}
// Remove from maps
this.extensions.delete(extensionId)
this.contexts.delete(extensionId)
}
/**
* Shutdown all extensions
*/
async shutdown(): Promise<void> {
console.log('[Extensions] Shutting down all extensions...')
for (const extensionId of this.extensions.keys()) {
try {
await this.unloadExtension(extensionId)
} catch (e) {
console.error(`[Extensions] Error unloading ${extensionId}:`, e)
}
}
console.log('[Extensions] All extensions shut down')
}
/**
* Get a loaded extension
*/
getExtension(extensionId: string): LoadedExtension | undefined {
return this.extensions.get(extensionId)
}
/**
* Get all loaded extensions
*/
getAllExtensions(): LoadedExtension[] {
return Array.from(this.extensions.values())
}
/**
* Check if an extension is loaded and ready
*/
isReady(extensionId: string): boolean {
const ext = this.extensions.get(extensionId)
return ext?.status === 'ready'
}
/**
* Get all registered RPC methods
*/
getRegisteredMethods(): Map<string, RegisteredMethod> {
return this.methodRegistry
}
/**
* Call an extension RPC method
*/
async callMethod(
methodName: string,
request: any,
applicationId: string,
userPubkey?: string
): Promise<any> {
const method = this.methodRegistry.get(methodName)
if (!method) {
throw new Error(`Unknown method: ${methodName}`)
}
const ext = this.extensions.get(method.extensionId)
if (!ext || ext.status !== 'ready') {
throw new Error(`Extension ${method.extensionId} not ready`)
}
return method.handler(request, applicationId, userPubkey)
}
/**
* Check if a method exists
*/
hasMethod(methodName: string): boolean {
return this.methodRegistry.has(methodName)
}
/**
* Dispatch payment received event to all extensions
*/
async dispatchPaymentReceived(payment: PaymentReceivedData): Promise<void> {
for (const context of this.contexts.values()) {
try {
await context.dispatchPaymentReceived(payment)
} catch (e) {
console.error('[Extensions] Error dispatching payment:', e)
}
}
}
/**
* Dispatch Nostr event to all extensions
*/
async dispatchNostrEvent(event: NostrEvent, applicationId: string): Promise<void> {
for (const context of this.contexts.values()) {
try {
await context.dispatchNostrEvent(event, applicationId)
} catch (e) {
console.error('[Extensions] Error dispatching Nostr event:', e)
}
}
}
/**
* Run health checks on all extensions
*/
async healthCheck(): Promise<Map<string, boolean>> {
const results = new Map<string, boolean>()
for (const [id, ext] of this.extensions.entries()) {
if (ext.status !== 'ready') {
results.set(id, false)
continue
}
try {
if (ext.instance.healthCheck) {
results.set(id, await ext.instance.healthCheck())
} else {
results.set(id, true)
}
} catch (e) {
results.set(id, false)
}
}
return results
}
/**
* Get extension status summary
*/
getStatus(): {
total: number
ready: number
error: number
extensions: Array<{ id: string; name: string; version: string; status: string }>
} {
const extensions = this.getAllExtensions().map(ext => ({
id: ext.info.id,
name: ext.info.name,
version: ext.info.version,
status: ext.status
}))
return {
total: extensions.length,
ready: extensions.filter(e => e.status === 'ready').length,
error: extensions.filter(e => e.status === 'error').length,
extensions
}
}
}
/**
* Create an extension loader instance
*/
export function createExtensionLoader(
config: ExtensionLoaderConfig,
mainHandler: MainHandlerInterface
): ExtensionLoader {
return new ExtensionLoader(config, mainHandler)
}

View file

@ -0,0 +1,311 @@
/**
* NIP-05 Extension for Lightning.Pub
*
* Implements Nostr NIP-05: Mapping Nostr keys to DNS-based internet identifiers
* Allows users to claim human-readable addresses like alice@domain.com
*
* Features:
* - Username claiming and management
* - .well-known/nostr.json endpoint
* - Optional relay hints
* - Admin controls for identity management
*/
import {
Extension,
ExtensionInfo,
ExtensionContext,
ExtensionDatabase,
HttpRoute,
HttpRequest,
HttpResponse
} from '../types.js'
import { runMigrations } from './migrations.js'
import { Nip05Manager } from './managers/nip05Manager.js'
import {
ClaimUsernameRequest,
UpdateRelaysRequest,
Nip05Config
} from './types.js'
/**
* NIP-05 Extension
*/
export default class Nip05Extension implements Extension {
readonly info: ExtensionInfo = {
id: 'nip05',
name: 'NIP-05 Identity',
version: '1.0.0',
description: 'Human-readable Nostr identities (username@domain)',
author: 'Lightning.Pub',
minPubVersion: '1.0.0'
}
private manager!: Nip05Manager
private ctx!: ExtensionContext
private config: Nip05Config = {}
/**
* Initialize the extension
*/
async initialize(ctx: ExtensionContext, db: ExtensionDatabase): Promise<void> {
this.ctx = ctx
// Run migrations
await runMigrations(db)
// Initialize manager
this.manager = new Nip05Manager(ctx, db, this.config)
// Register RPC methods
this.registerRpcMethods(ctx)
ctx.log('info', 'Extension initialized')
}
/**
* Shutdown the extension
*/
async shutdown(): Promise<void> {
// Cleanup if needed
}
/**
* Configure the extension
*/
configure(config: Nip05Config): void {
this.config = config
}
/**
* Get HTTP routes for this extension
* These need to be mounted by the main HTTP server
*/
getHttpRoutes(): HttpRoute[] {
return [
// NIP-05 well-known endpoint
{
method: 'GET',
path: '/.well-known/nostr.json',
handler: this.handleNostrJson.bind(this)
},
// Alternative path for proxied setups
{
method: 'GET',
path: '/api/v1/nip05/nostr.json',
handler: this.handleNostrJson.bind(this)
},
// Lightning Address endpoint (LUD-16)
// Makes NIP-05 usernames work as Lightning Addresses for zaps
{
method: 'GET',
path: '/.well-known/lnurlp/:username',
handler: this.handleLnurlPay.bind(this)
}
]
}
/**
* Register RPC methods with the extension context
*/
private registerRpcMethods(ctx: ExtensionContext): void {
// Claim a username
ctx.registerMethod('nip05.claim', async (req, appId, userId, pubkey) => {
if (!userId || !pubkey) {
throw new Error('Authentication required')
}
return this.manager.claimUsername(userId, pubkey, appId, req as ClaimUsernameRequest)
})
// Release your username
ctx.registerMethod('nip05.release', async (req, appId, userId) => {
if (!userId) {
throw new Error('Authentication required')
}
await this.manager.releaseUsername(userId, appId)
return { success: true }
})
// Update your relays
ctx.registerMethod('nip05.updateRelays', async (req, appId, userId) => {
if (!userId) {
throw new Error('Authentication required')
}
const identity = await this.manager.updateRelays(userId, appId, req as UpdateRelaysRequest)
return { identity }
})
// Get your identity
ctx.registerMethod('nip05.getMyIdentity', async (req, appId, userId) => {
if (!userId) {
throw new Error('Authentication required')
}
return this.manager.getMyIdentity(userId, appId)
})
// Look up a username (public)
ctx.registerMethod('nip05.lookup', async (req, appId) => {
return this.manager.lookupUsername(appId, req.username)
})
// Look up by pubkey (public)
ctx.registerMethod('nip05.lookupByPubkey', async (req, appId) => {
return this.manager.lookupByPubkey(appId, req.pubkey)
})
// List all identities (admin)
ctx.registerMethod('nip05.listIdentities', async (req, appId) => {
return this.manager.listIdentities(appId, {
limit: req.limit,
offset: req.offset,
activeOnly: req.active_only
})
})
// Deactivate an identity (admin)
ctx.registerMethod('nip05.deactivate', async (req, appId) => {
await this.manager.deactivateIdentity(appId, req.identity_id)
return { success: true }
})
// Reactivate an identity (admin)
ctx.registerMethod('nip05.reactivate', async (req, appId) => {
await this.manager.reactivateIdentity(appId, req.identity_id)
return { success: true }
})
}
// =========================================================================
// HTTP Route Handlers
// =========================================================================
/**
* Handle /.well-known/nostr.json request
* GET /.well-known/nostr.json?name=<username>
*
* Per NIP-05 spec, returns:
* {
* "names": { "<username>": "<pubkey hex>" },
* "relays": { "<pubkey hex>": ["wss://..."] }
* }
*/
/**
* NIP-05 spec: "The /.well-known/nostr.json endpoint MUST NOT return any
* HTTP redirects." This extension always returns direct 200/4xx/5xx responses.
* Deployment note: ensure reverse proxies do not add 3xx redirects on this path
* (e.g. HTTPHTTPS or trailing-slash redirects).
*/
private async handleNostrJson(req: HttpRequest): Promise<HttpResponse> {
try {
// Get application ID from request context
// In a multi-tenant setup, this would come from the host or path
const appId = req.headers['x-application-id'] || 'default'
// Set domain from request host for NIP-05 address formatting
if (req.headers['host']) {
this.manager.setDomain(req.headers['host'].split(':')[0])
}
// Get the name parameter
const name = req.query.name
// Get the JSON response
const response = await this.manager.handleNostrJson(appId, name)
return {
status: 200,
body: response,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'max-age=300' // Cache for 5 minutes
}
}
} catch (error) {
this.ctx.log('error', `Error handling nostr.json: ${error}`)
return {
status: 500,
body: { error: 'Internal server error' },
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
}
}
}
/**
* Handle /.well-known/lnurlp/:username request (Lightning Address / LUD-16)
*
* This enables NIP-05 usernames to work as Lightning Addresses for receiving
* payments and zaps. When someone sends to alice@domain.com:
* 1. Wallet resolves /.well-known/lnurlp/alice
* 2. We look up alice -> pubkey in our NIP-05 database
* 3. We return LNURL-pay info from Lightning.Pub for that user
*/
private async handleLnurlPay(req: HttpRequest): Promise<HttpResponse> {
try {
const { username } = req.params
const appId = req.headers['x-application-id'] || 'default'
if (!username) {
return {
status: 400,
body: { status: 'ERROR', reason: 'Username required' },
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
}
}
// Look up the username in our NIP-05 database
const lookup = await this.manager.lookupUsername(appId, username)
if (!lookup.found || !lookup.identity) {
return {
status: 404,
body: { status: 'ERROR', reason: 'User not found' },
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
}
}
// Get LNURL-pay info from Lightning.Pub for this user's pubkey
const lnurlPayInfo = await this.ctx.getLnurlPayInfo(lookup.identity.pubkey_hex, {
description: `Pay to ${username}`
})
// NIP-57: ensure zap support fields are present for wallet compatibility
if (!lnurlPayInfo.allowsNostr || !lnurlPayInfo.nostrPubkey) {
this.ctx.log('warn', `LNURL-pay response for ${username} missing zap fields (allowsNostr=${lnurlPayInfo.allowsNostr}, nostrPubkey=${!!lnurlPayInfo.nostrPubkey}). Zaps will not work.`)
}
return {
status: 200,
body: lnurlPayInfo,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'max-age=60' // Cache for 1 minute
}
}
} catch (error) {
this.ctx.log('error', `Error handling lnurlp: ${error}`)
return {
status: 500,
body: { status: 'ERROR', reason: 'Internal server error' },
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
}
}
}
}
// Export types for external use
export * from './types.js'
export { Nip05Manager } from './managers/nip05Manager.js'

View file

@ -0,0 +1,467 @@
/**
* NIP-05 Identity Manager
*
* Handles username claiming, lookup, and .well-known/nostr.json responses
*/
import { ExtensionContext, ExtensionDatabase } from '../../types.js'
import {
Nip05Identity,
Nip05IdentityRow,
Nip05JsonResponse,
Nip05Config,
UsernameValidation,
ClaimUsernameRequest,
ClaimUsernameResponse,
UpdateRelaysRequest,
LookupUsernameResponse,
GetMyIdentityResponse
} from '../types.js'
import crypto from 'crypto'
/**
* Default configuration
*/
const DEFAULT_CONFIG: Required<Nip05Config> = {
max_username_length: 30,
min_username_length: 1,
reserved_usernames: ['admin', 'root', 'system', 'support', 'help', 'info', 'contact', 'abuse', 'postmaster', 'webmaster', 'hostmaster', 'noreply', 'no-reply', 'null', 'undefined', 'api', 'www', 'mail', 'ftp', 'ssh', 'test', 'demo'],
include_relays: true,
default_relays: [],
max_relays_per_user: 10,
max_identities_listing: 100
}
/**
* Convert database row to Nip05Identity
*/
function rowToIdentity(row: Nip05IdentityRow): Nip05Identity {
return {
id: row.id,
application_id: row.application_id,
user_id: row.user_id,
username: row.username,
pubkey_hex: row.pubkey_hex,
relays: JSON.parse(row.relays_json),
is_active: row.is_active === 1,
created_at: row.created_at,
updated_at: row.updated_at
}
}
/**
* Generate a unique ID
*/
function generateId(): string {
return crypto.randomBytes(16).toString('hex')
}
/**
* Validate username format per NIP-05 spec
* - Characters allowed: a-z, 0-9, hyphen (-), underscore (_), period (.)
* - Must start with a letter
* - Must not end with a hyphen, underscore, or period
* - Length within bounds
* - Special case: "_" alone is the root identifier (_@domain)
*/
function validateUsername(username: string, config: Required<Nip05Config>): UsernameValidation {
if (!username) {
return { valid: false, error: 'Username is required' }
}
const normalized = username.toLowerCase().trim()
// Special case: root identifier "_" per NIP-05
if (normalized === '_') {
return { valid: true }
}
if (normalized.length < config.min_username_length) {
return { valid: false, error: `Username must be at least ${config.min_username_length} character(s)` }
}
if (normalized.length > config.max_username_length) {
return { valid: false, error: `Username must be at most ${config.max_username_length} characters` }
}
// NIP-05 spec: local-part MUST only use characters a-z0-9-_.
// Must start with a letter, must not end with separator
if (!/^[a-z][a-z0-9._-]*[a-z0-9]$/.test(normalized) && !/^[a-z]$/.test(normalized)) {
return { valid: false, error: 'Username must start with a letter, end with a letter or number, and contain only a-z, 0-9, hyphens, underscores, and periods' }
}
// Check reserved usernames
if (config.reserved_usernames.includes(normalized)) {
return { valid: false, error: 'This username is reserved' }
}
return { valid: true }
}
/**
* Validate relay URLs
*/
function validateRelays(relays: string[], maxRelays?: number): UsernameValidation {
if (!Array.isArray(relays)) {
return { valid: false, error: 'Relays must be an array' }
}
if (maxRelays && relays.length > maxRelays) {
return { valid: false, error: `Too many relays (max ${maxRelays})` }
}
for (const relay of relays) {
if (typeof relay !== 'string') {
return { valid: false, error: 'Each relay must be a string' }
}
if (!relay.startsWith('wss://') && !relay.startsWith('ws://')) {
return { valid: false, error: `Invalid relay URL: ${relay}` }
}
}
return { valid: true }
}
export class Nip05Manager {
private ctx: ExtensionContext
private db: ExtensionDatabase
private config: Required<Nip05Config>
private domain: string
constructor(ctx: ExtensionContext, db: ExtensionDatabase, config?: Nip05Config) {
this.ctx = ctx
this.db = db
this.config = { ...DEFAULT_CONFIG, ...config }
// Extract domain from the service URL
this.domain = this.extractDomain()
}
/**
* Extract domain from service URL for NIP-05 addresses
*/
private extractDomain(): string {
// This would come from Lightning.Pub's configuration
// For now, we'll derive it when needed from the request host
return 'localhost'
}
/**
* Set the domain (called from HTTP request context)
*/
setDomain(domain: string): void {
this.domain = domain
}
/**
* Claim a username for the current user
*/
async claimUsername(
userId: string,
pubkeyHex: string,
applicationId: string,
request: ClaimUsernameRequest
): Promise<ClaimUsernameResponse> {
const normalizedUsername = request.username.toLowerCase().trim()
// Validate username format
const validation = validateUsername(normalizedUsername, this.config)
if (!validation.valid) {
throw new Error(validation.error)
}
// Validate relays if provided
const relays = request.relays || this.config.default_relays
if (relays.length > 0) {
const relayValidation = validateRelays(relays, this.config.max_relays_per_user)
if (!relayValidation.valid) {
throw new Error(relayValidation.error)
}
}
// Check if user already has an identity in this application
const existingByUser = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND user_id = ?`,
[applicationId, userId]
)
if (existingByUser.length > 0) {
throw new Error('You already have a username. Release it first to claim a new one.')
}
// Check if username is already taken
const existingByUsername = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND username = ?`,
[applicationId, normalizedUsername]
)
if (existingByUsername.length > 0) {
throw new Error('This username is already taken')
}
// Create the identity
const now = Math.floor(Date.now() / 1000)
const id = generateId()
await this.db.execute(
`INSERT INTO identities (id, application_id, user_id, username, pubkey_hex, relays_json, is_active, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
[id, applicationId, userId, normalizedUsername, pubkeyHex, JSON.stringify(relays), now, now]
)
const identity: Nip05Identity = {
id,
application_id: applicationId,
user_id: userId,
username: normalizedUsername,
pubkey_hex: pubkeyHex,
relays,
is_active: true,
created_at: now,
updated_at: now
}
return {
identity,
nip05_address: `${normalizedUsername}@${this.domain}`
}
}
/**
* Release (delete) the current user's username
*/
async releaseUsername(userId: string, applicationId: string): Promise<void> {
const result = await this.db.execute(
`DELETE FROM identities WHERE application_id = ? AND user_id = ?`,
[applicationId, userId]
)
if (result.changes === 0) {
throw new Error('You do not have a username to release')
}
}
/**
* Update relays for the current user's identity
*/
async updateRelays(
userId: string,
applicationId: string,
request: UpdateRelaysRequest
): Promise<Nip05Identity> {
// Validate relays
const validation = validateRelays(request.relays, this.config.max_relays_per_user)
if (!validation.valid) {
throw new Error(validation.error)
}
const now = Math.floor(Date.now() / 1000)
const result = await this.db.execute(
`UPDATE identities SET relays_json = ?, updated_at = ? WHERE application_id = ? AND user_id = ?`,
[JSON.stringify(request.relays), now, applicationId, userId]
)
if (result.changes === 0) {
throw new Error('You do not have a username')
}
// Fetch and return the updated identity
const rows = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND user_id = ?`,
[applicationId, userId]
)
return rowToIdentity(rows[0])
}
/**
* Get the current user's identity
*/
async getMyIdentity(userId: string, applicationId: string): Promise<GetMyIdentityResponse> {
const rows = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND user_id = ?`,
[applicationId, userId]
)
if (rows.length === 0) {
return { has_identity: false }
}
const identity = rowToIdentity(rows[0])
return {
has_identity: true,
identity,
nip05_address: `${identity.username}@${this.domain}`
}
}
/**
* Look up a username (public, no auth required)
*/
async lookupUsername(applicationId: string, username: string): Promise<LookupUsernameResponse> {
const normalizedUsername = username.toLowerCase().trim()
const rows = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND username = ? AND is_active = 1`,
[applicationId, normalizedUsername]
)
if (rows.length === 0) {
return { found: false }
}
const identity = rowToIdentity(rows[0])
return {
found: true,
identity,
nip05_address: `${identity.username}@${this.domain}`
}
}
/**
* Look up by pubkey
*/
async lookupByPubkey(applicationId: string, pubkeyHex: string): Promise<LookupUsernameResponse> {
const rows = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND pubkey_hex = ? AND is_active = 1`,
[applicationId, pubkeyHex]
)
if (rows.length === 0) {
return { found: false }
}
const identity = rowToIdentity(rows[0])
return {
found: true,
identity,
nip05_address: `${identity.username}@${this.domain}`
}
}
/**
* Handle /.well-known/nostr.json request
* This is the core NIP-05 endpoint
*/
async handleNostrJson(applicationId: string, name?: string): Promise<Nip05JsonResponse> {
const response: Nip05JsonResponse = {
names: {}
}
if (this.config.include_relays) {
response.relays = {}
}
if (name) {
// Look up specific username
const normalizedName = name.toLowerCase().trim()
const rows = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND username = ? AND is_active = 1`,
[applicationId, normalizedName]
)
if (rows.length > 0) {
const identity = rowToIdentity(rows[0])
response.names[identity.username] = identity.pubkey_hex
if (this.config.include_relays && identity.relays.length > 0) {
response.relays![identity.pubkey_hex] = identity.relays
}
}
} else {
// Return all active identities (with configurable limit)
const limit = this.config.max_identities_listing
const rows = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND is_active = 1 LIMIT ?`,
[applicationId, limit]
)
for (const row of rows) {
const identity = rowToIdentity(row)
response.names[identity.username] = identity.pubkey_hex
if (this.config.include_relays && identity.relays.length > 0) {
response.relays![identity.pubkey_hex] = identity.relays
}
}
}
return response
}
/**
* List all identities for an application (admin)
*/
async listIdentities(
applicationId: string,
options?: { limit?: number; offset?: number; activeOnly?: boolean }
): Promise<{ identities: Nip05Identity[]; total: number }> {
const limit = options?.limit || 50
const offset = options?.offset || 0
const activeClause = options?.activeOnly !== false ? 'AND is_active = 1' : ''
// Get total count
const countResult = await this.db.query<{ count: number }>(
`SELECT COUNT(*) as count FROM identities WHERE application_id = ? ${activeClause}`,
[applicationId]
)
const total = countResult[0]?.count || 0
// Get page of results
const rows = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? ${activeClause}
ORDER BY created_at DESC LIMIT ? OFFSET ?`,
[applicationId, limit, offset]
)
return {
identities: rows.map(rowToIdentity),
total
}
}
/**
* Deactivate an identity (admin)
*/
async deactivateIdentity(applicationId: string, identityId: string): Promise<void> {
const now = Math.floor(Date.now() / 1000)
const result = await this.db.execute(
`UPDATE identities SET is_active = 0, updated_at = ? WHERE application_id = ? AND id = ?`,
[now, applicationId, identityId]
)
if (result.changes === 0) {
throw new Error('Identity not found')
}
}
/**
* Reactivate an identity (admin)
*/
async reactivateIdentity(applicationId: string, identityId: string): Promise<void> {
const now = Math.floor(Date.now() / 1000)
// Check if username is taken by an active identity
const identity = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE id = ? AND application_id = ?`,
[identityId, applicationId]
)
if (identity.length === 0) {
throw new Error('Identity not found')
}
const conflicting = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND username = ? AND is_active = 1 AND id != ?`,
[applicationId, identity[0].username, identityId]
)
if (conflicting.length > 0) {
throw new Error('Username is already taken by another active identity')
}
await this.db.execute(
`UPDATE identities SET is_active = 1, updated_at = ? WHERE application_id = ? AND id = ?`,
[now, applicationId, identityId]
)
}
}

View file

@ -0,0 +1,93 @@
/**
* NIP-05 Extension Database Migrations
*/
import { ExtensionDatabase } from '../types.js'
export interface Migration {
version: number
name: string
up: (db: ExtensionDatabase) => Promise<void>
down?: (db: ExtensionDatabase) => Promise<void>
}
export const migrations: Migration[] = [
{
version: 1,
name: 'create_identities_table',
up: async (db: ExtensionDatabase) => {
await db.execute(`
CREATE TABLE IF NOT EXISTS identities (
id TEXT PRIMARY KEY,
application_id TEXT NOT NULL,
user_id TEXT NOT NULL,
-- Identity mapping
username TEXT NOT NULL,
pubkey_hex TEXT NOT NULL,
-- Optional relays (JSON array)
relays_json TEXT NOT NULL DEFAULT '[]',
-- Status
is_active INTEGER NOT NULL DEFAULT 1,
-- Timestamps
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
)
`)
// Unique username per application (case-insensitive via lowercase storage)
await db.execute(`
CREATE UNIQUE INDEX IF NOT EXISTS idx_identities_username_app
ON identities(application_id, username)
`)
// One identity per user per application
await db.execute(`
CREATE UNIQUE INDEX IF NOT EXISTS idx_identities_user_app
ON identities(application_id, user_id)
`)
// Look up by pubkey
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_identities_pubkey
ON identities(pubkey_hex)
`)
// Look up active identities for .well-known endpoint
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_identities_active
ON identities(application_id, is_active, username)
`)
}
}
]
/**
* Run all pending migrations
*/
export async function runMigrations(db: ExtensionDatabase): Promise<void> {
// Get current version
const versionResult = await db.query<{ value: string }>(
`SELECT value FROM _extension_meta WHERE key = 'migration_version'`
).catch(() => [])
const currentVersion = versionResult.length > 0 ? parseInt(versionResult[0].value, 10) : 0
// Run pending migrations
for (const migration of migrations) {
if (migration.version > currentVersion) {
console.log(`[NIP-05] Running migration ${migration.version}: ${migration.name}`)
await migration.up(db)
// Update version
await db.execute(
`INSERT INTO _extension_meta (key, value) VALUES ('migration_version', ?)
ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
[String(migration.version)]
)
}
}
}

View file

@ -0,0 +1,136 @@
/**
* NIP-05 Extension Types
*
* Implements Nostr NIP-05: Mapping Nostr keys to DNS-based internet identifiers
* Allows users to have human-readable addresses like alice@domain.com
*/
/**
* A NIP-05 identity mapping a username to a Nostr public key
*/
export interface Nip05Identity {
id: string
application_id: string
user_id: string
/** The human-readable username (lowercase, alphanumeric + underscore) */
username: string
/** The Nostr public key in hex format */
pubkey_hex: string
/** Optional list of relay URLs for this user */
relays: string[]
/** Whether this identity is active */
is_active: boolean
created_at: number
updated_at: number
}
/**
* NIP-05 JSON response format per the spec
* GET /.well-known/nostr.json?name=<username>
*/
export interface Nip05JsonResponse {
names: Record<string, string>
relays?: Record<string, string[]>
}
/**
* Request to claim a username
*/
export interface ClaimUsernameRequest {
username: string
relays?: string[]
}
/**
* Response after claiming a username
*/
export interface ClaimUsernameResponse {
identity: Nip05Identity
nip05_address: string
}
/**
* Request to update relays for a username
*/
export interface UpdateRelaysRequest {
relays: string[]
}
/**
* Request to look up a username
*/
export interface LookupUsernameRequest {
username: string
}
/**
* Response for username lookup
*/
export interface LookupUsernameResponse {
found: boolean
identity?: Nip05Identity
nip05_address?: string
}
/**
* Response for getting current user's identity
*/
export interface GetMyIdentityResponse {
has_identity: boolean
identity?: Nip05Identity
nip05_address?: string
}
/**
* Database row for NIP-05 identity
*/
export interface Nip05IdentityRow {
id: string
application_id: string
user_id: string
username: string
pubkey_hex: string
relays_json: string
is_active: number
created_at: number
updated_at: number
}
/**
* Extension configuration
*/
export interface Nip05Config {
/** Maximum username length (default: 30) */
max_username_length?: number
/** Minimum username length (default: 1) */
min_username_length?: number
/** Reserved usernames that cannot be claimed */
reserved_usernames?: string[]
/** Whether to include relays in the JSON response (default: true) */
include_relays?: boolean
/** Default relays to suggest for new users */
default_relays?: string[]
/** Maximum number of relays per user (default: 10) */
max_relays_per_user?: number
/** Maximum number of identities returned when no name query param is provided (default: 100) */
max_identities_listing?: number
}
/**
* Validation result for username
*/
export interface UsernameValidation {
valid: boolean
error?: string
}

285
src/extensions/types.ts Normal file
View file

@ -0,0 +1,285 @@
/**
* Extension System Core Types
*
* These types define the contract between Lightning.Pub and extensions.
*/
/**
* Extension metadata
*/
export interface ExtensionInfo {
id: string // Unique identifier (lowercase, no spaces)
name: string // Display name
version: string // Semver version
description: string // Short description
author: string // Author name or organization
minPubVersion?: string // Minimum Lightning.Pub version required
dependencies?: string[] // Other extension IDs this depends on
}
/**
* Extension database interface
* Provides isolated database access for each extension
*/
export interface ExtensionDatabase {
/**
* Execute a write query (INSERT, UPDATE, DELETE, CREATE, etc.)
*/
execute(sql: string, params?: any[]): Promise<{ changes?: number; lastId?: number }>
/**
* Execute a read query (SELECT)
*/
query<T = any>(sql: string, params?: any[]): Promise<T[]>
/**
* Execute multiple statements in a transaction
*/
transaction<T>(fn: () => Promise<T>): Promise<T>
}
/**
* Application info provided to extensions
*/
export interface ApplicationInfo {
id: string
name: string
nostr_public: string // Application's Nostr pubkey (hex)
balance_sats: number
}
/**
* Invoice creation options
*/
export interface CreateInvoiceOptions {
memo?: string
expiry?: number // Seconds until expiry
metadata?: Record<string, any> // Custom metadata for callbacks
}
/**
* Created invoice result
*/
export interface CreatedInvoice {
id: string // Internal invoice ID
paymentRequest: string // BOLT11 invoice string
paymentHash: string // Payment hash (hex)
expiry: number // Expiry timestamp
}
/**
* Payment received callback data
*/
export interface PaymentReceivedData {
invoiceId: string
paymentHash: string
amountSats: number
metadata?: Record<string, any>
}
/**
* LNURL-pay info response (LUD-06/LUD-16)
* Used for Lightning Address and zap support
*/
export interface LnurlPayInfo {
tag: 'payRequest'
callback: string // URL to call with amount
minSendable: number // Minimum msats
maxSendable: number // Maximum msats
metadata: string // JSON-encoded metadata array
allowsNostr?: boolean // Whether zaps are supported
nostrPubkey?: string // Pubkey for zap receipts (hex)
}
/**
* Nostr event structure (minimal)
*/
export interface NostrEvent {
id: string
pubkey: string
created_at: number
kind: number
tags: string[][]
content: string
sig?: string
}
/**
* Unsigned Nostr event for publishing
*/
export interface UnsignedNostrEvent {
kind: number
pubkey: string
created_at: number
tags: string[][]
content: string
}
/**
* RPC method handler function
*/
export type RpcMethodHandler = (
request: any,
applicationId: string,
userPubkey?: string
) => Promise<any>
/**
* Extension context - interface provided to extensions for interacting with Lightning.Pub
*/
export interface ExtensionContext {
/**
* Get information about an application
*/
getApplication(applicationId: string): Promise<ApplicationInfo | null>
/**
* Create a Lightning invoice
*/
createInvoice(amountSats: number, options?: CreateInvoiceOptions): Promise<CreatedInvoice>
/**
* Pay a Lightning invoice (requires sufficient balance)
*/
payInvoice(applicationId: string, paymentRequest: string, maxFeeSats?: number): Promise<{
paymentHash: string
feeSats: number
}>
/**
* Send an encrypted DM via Nostr (NIP-44)
*/
sendEncryptedDM(applicationId: string, recipientPubkey: string, content: string): Promise<string>
/**
* Publish a Nostr event (signed by application's key)
*/
publishNostrEvent(event: UnsignedNostrEvent): Promise<string | null>
/**
* Get LNURL-pay info for a user (by pubkey)
* Used to enable Lightning Address support (LUD-16) and zaps (NIP-57)
*/
getLnurlPayInfo(pubkeyHex: string, options?: {
metadata?: string // Custom metadata JSON
description?: string // Human-readable description
}): Promise<LnurlPayInfo>
/**
* Subscribe to payment received callbacks
*/
onPaymentReceived(callback: (payment: PaymentReceivedData) => Promise<void>): void
/**
* Subscribe to incoming Nostr events for the application
*/
onNostrEvent(callback: (event: NostrEvent, applicationId: string) => Promise<void>): void
/**
* Register an RPC method
*/
registerMethod(name: string, handler: RpcMethodHandler): void
/**
* Get the extension's isolated database
*/
getDatabase(): ExtensionDatabase
/**
* Log a message (prefixed with extension ID)
*/
log(level: 'debug' | 'info' | 'warn' | 'error', message: string, ...args: any[]): void
}
/**
* HTTP route handler types
* Used by extensions that expose HTTP endpoints (e.g. LNURL, .well-known)
*/
export interface HttpRequest {
method: string
path: string
params: Record<string, string>
query: Record<string, string>
headers: Record<string, string>
body?: any
}
export interface HttpResponse {
status: number
body: any
headers?: Record<string, string>
}
export interface HttpRoute {
method: 'GET' | 'POST'
path: string
handler: (req: HttpRequest) => Promise<HttpResponse>
}
/**
* Extension interface - what extensions must implement
*/
export interface Extension {
/**
* Extension metadata
*/
readonly info: ExtensionInfo
/**
* Initialize the extension
* Called once when the extension is loaded
*/
initialize(ctx: ExtensionContext, db: ExtensionDatabase): Promise<void>
/**
* Shutdown the extension
* Called when Lightning.Pub is shutting down
*/
shutdown?(): Promise<void>
/**
* Health check
* Return true if extension is healthy
*/
healthCheck?(): Promise<boolean>
/**
* Get HTTP routes exposed by this extension
* The main HTTP server will mount these routes
*/
getHttpRoutes?(): HttpRoute[]
}
/**
* Extension constructor type
*/
export type ExtensionConstructor = new () => Extension
/**
* Extension module default export
*/
export interface ExtensionModule {
default: ExtensionConstructor
}
/**
* Loaded extension state
*/
export interface LoadedExtension {
info: ExtensionInfo
instance: Extension
database: ExtensionDatabase
status: 'loading' | 'ready' | 'error' | 'stopped'
error?: Error
loadedAt: number
}
/**
* Extension loader configuration
*/
export interface ExtensionLoaderConfig {
extensionsDir: string // Directory containing extensions
databaseDir: string // Directory for extension databases
enabledExtensions?: string[] // If set, only load these extensions
disabledExtensions?: string[] // Extensions to skip
}

View file

@ -105,7 +105,7 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
return {
Stop: () => { mainHandler.adminManager.setNostrConnected(false); return nostr.Stop },
Send: (...args) => nostr.Send(...args),
Send: async (...args) => nostr.Send(...args),
Ping: () => nostr.Ping(),
Reset: (settings: NostrSettings) => nostr.Reset(settings)
}

View file

@ -202,7 +202,7 @@ export default class {
cooperative: true,
fundingCanceled: true,
localForce: true,
remoteForce: true
remoteForce: true,
}, DeadLineMetadata())
return res.response
}
@ -289,6 +289,8 @@ export default class {
account: "",
endHeight: 0,
startHeight: this.latestKnownBlockHeigh,
indexOffset: 0,
maxTransactions: 0
}, { abort: this.abortController.signal })
stream.responses.onMessage(tx => {
if (tx.blockHeight > this.latestKnownBlockHeigh) {
@ -542,7 +544,7 @@ export default class {
async GetTransactions(startHeight: number): Promise<TransactionDetails> {
this.log(DEBUG, "Getting transactions")
const res = await this.lightning.getTransactions({ startHeight, endHeight: 0, account: "", }, DeadLineMetadata())
const res = await this.lightning.getTransactions({ startHeight, endHeight: 0, account: "", indexOffset: 0, maxTransactions: 0 }, DeadLineMetadata())
return res.response
}
@ -566,6 +568,7 @@ export default class {
inboundFee: undefined,
feeRatePpm: policy.fee_rate_ppm,
minHtlcMsatSpecified: policy.min_htlc_msat > 0,
createMissingEdge: false,
}, DeadLineMetadata())
return res.response
}
@ -651,7 +654,8 @@ export default class {
txidBytes: Buffer.alloc(0)
},
force: false,
satPerByte: 0
satPerByte: 0,
deadlineDelta: 0
}, DeadLineMetadata())
return res.response
}

View file

@ -25,5 +25,7 @@ export const PayInvoiceReq = (invoice: string, amount: number, feeLimit: number)
paymentHash: Buffer.alloc(0),
routeHints: [],
timePref: 0,
outgoingChanId: '0'
outgoingChanId: '0',
cancelable: false,
firstHopCustomRecords: {}
})

View file

@ -99,6 +99,9 @@ export class Swaps {
quote: this.mapInvoiceSwapQuote(s),
failure_reason: s.failure_reason,
completed_at_unix: s.completed_at_unix || 1,
refund_address: s.refund_address,
refund_at_unix: s.refund_at_unix,
refund_tx_id: s.refund_tx_id,
}))
return {
current_block_height: currentBlockHeight,
@ -132,6 +135,7 @@ export class Swaps {
if (!result.ok) {
throw new Error(result.error)
}
await this.storage.paymentStorage.UpdateRefundInvoiceSwap(swapOperationId, refundAddress, result.publish.txId)
if (result.publish.done) {
return { published: true, txId: result.publish.txId }
}
@ -169,7 +173,7 @@ export class Swaps {
await this.storage.paymentStorage.FinalizeInvoiceSwap(swapOpId)
this.log("invoice swap completed", { swapOpId, txId })
} else {
await this.storage.paymentStorage.FailInvoiceSwap(swapOpId, result.error, txId)
await this.storage.paymentStorage.FailInvoiceSwap(swapOpId, result.error)
this.log("invoice swap failed", { swapOpId, error: result.error })
}
}, () => payAddress(swap.address, swap.transaction_amount)

View file

@ -183,14 +183,23 @@ export default class {
}
this.log("Deleting user", userId, "progress", i + 1, "/", toDelete.length)
await this.storage.StartTransaction(async tx => {
for (const appUserId of appUserIds) {
for (let j = 0; j < appUserIds.length; j++) {
const appUserId = appUserIds[j]
this.log("Deleting app user", appUserId, "progress", j + 1, "/", appUserIds.length)
this.log("Removing user grants")
await this.storage.managementStorage.removeUserGrants(appUserId, tx)
this.log("Removing user offers")
await this.storage.offerStorage.DeleteUserOffers(appUserId, tx)
this.log("Removing user debit access")
await this.storage.debitStorage.RemoveUserDebitAccess(appUserId, tx)
this.log("Removing user devices")
await this.storage.applicationStorage.RemoveAppUserDevices(appUserId, tx)
}
this.log("Removing user invoices")
await this.storage.paymentStorage.RemoveUserInvoices(userId, tx)
this.log("Removing user products")
await this.storage.productStorage.RemoveUserProducts(userId, tx)
this.log("Removing user ephemeral keys")
await this.storage.paymentStorage.RemoveUserEphemeralKeys(userId, tx)
await this.storage.paymentStorage.RemoveUserInvoicePayments(userId, tx)
await this.storage.paymentStorage.RemoveUserTransactionPayments(userId, tx)

View file

@ -194,7 +194,7 @@ export default class {
const cbUrl = req.http_callback_url || receiver.callback_url || ""
let zapInfo: ZapInfo | undefined = undefined
if (req.invoice_req.zap) {
zapInfo = this.paymentManager.validateZapEvent(req.invoice_req.zap, req.invoice_req.amountSats)
zapInfo = this.paymentManager.validateZapEvent(req.invoice_req.zap, req.invoice_req.amountSats * 1000)
}
const expiry = req.invoice_req.expiry ? Math.min(req.invoice_req.expiry, defaultInvoiceExpiry) : defaultInvoiceExpiry
const opts: InboundOptionals = {
@ -241,6 +241,8 @@ export default class {
const paid = await this.paymentManager.PayInvoice(appUser.user.user_id, req, app, {
ack: pendingOp => { this.notifyAppUserPayment(appUser, pendingOp) }
})
// Refresh appUser balance from DB so notification has accurate latest_balance
appUser.user.balance_sats = paid.latest_balance
this.notifyAppUserPayment(appUser, paid.operation)
getLogger({ appName: app.name })(appUser.identifier, "invoice paid", paid.amount_paid, "sats")
return paid

View file

@ -153,13 +153,14 @@ export class DebitManager {
}
notifyPaymentSuccess = (debitRes: NdebitSuccess, event: { pub: string, id: string, appId: string }) => {
this.logger("✅ [DEBIT REQUEST] Payment successful, sending OK response to", event.pub.slice(0, 16) + "...", "for event", event.id.slice(0, 16) + "...")
this.sendDebitResponse(debitRes, event)
}
sendDebitResponse = (debitRes: NdebitFailure | NdebitSuccess, event: { pub: string, id: string, appId: string }) => {
this.logger("📤 [DEBIT RESPONSE] Sending Kind 21002 response:", JSON.stringify(debitRes), "to", event.pub.slice(0, 16) + "...")
const e = newNdebitResponse(JSON.stringify(debitRes), event)
this.storage.NostrSender().Send({ type: 'app', appId: event.appId }, { type: 'event', event: e, encrypt: { toPub: event.pub } })
}
payNdebitInvoice = async (event: NostrEvent, pointerdata: NdebitData): Promise<HandleNdebitRes> => {

View file

@ -162,7 +162,7 @@ export default class {
NewBlockHandler = async (height: number, skipMetrics?: boolean) => {
let confirmed: (PendingTx & { confs: number; })[]
let log = getLogger({})
log("NewBlockHandler called", JSON.stringify({ height, skipMetrics }))
// log("NewBlockHandler called", JSON.stringify({ height, skipMetrics }))
this.storage.paymentStorage.DeleteExpiredTransactionSwaps(height)
.catch(err => log(ERROR, "failed to delete expired transaction swaps", err.message || err))
this.storage.paymentStorage.DeleteExpiredInvoiceSwaps(height)
@ -178,7 +178,9 @@ export default class {
log(ERROR, "failed to check transactions after new block", err.message || err)
return
}
if (confirmed.length > 0) {
log("NewBlockHandler new confirmed transactions", confirmed.length)
}
await Promise.all(confirmed.map(async c => {
if (c.type === 'outgoing') {
await this.storage.paymentStorage.UpdateUserTransactionPayment(c.tx.serial_id, { confs: c.confs })

View file

@ -987,7 +987,9 @@ export default class {
async CheckNewlyConfirmedTxs() {
const pending = await this.storage.paymentStorage.GetPendingTransactions()
let log = getLogger({})
if (pending.incoming.length > 0 || pending.outgoing.length > 0) {
log("CheckNewlyConfirmedTxs ", pending.incoming.length, "incoming", pending.outgoing.length, "outgoing")
}
const confirmedIncoming: (PendingTx & { confs: number })[] = []
const confirmedOutgoing: (PendingTx & { confs: number })[] = []
for (const tx of pending.incoming) {

View file

@ -12,12 +12,15 @@ import HtlcTracker from './htlcTracker.js'
import { getLogger } from '../helpers/logger.js'
import { encodeTLV, usageMetricsToTlv } from '../helpers/tlv.js'
import { ChannelCloseSummary_ClosureType } from '../../../proto/lnd/lightning.js'
const cacheTTL = 1000 * 60 * 5 // 5 minutes
export default class Handler {
storage: Storage
lnd: LND
htlcTracker: HtlcTracker
appsMetricsCache: CacheController<Types.AppsMetrics> = new CacheController<Types.AppsMetrics>()
lndForwardingMetricsCache: CacheController<Types.LndForwardingMetrics> = new CacheController<Types.LndForwardingMetrics>()
lndMetricsCache: CacheController<Types.LndMetrics> = new CacheController<Types.LndMetrics>()
logger = getLogger({ component: "metrics" })
constructor(storage: Storage, lnd: LND) {
this.storage = storage
@ -183,8 +186,6 @@ export default class Handler {
})
}
/* addTrackedMetric = (appId: string, method: string, metric: Uint8Array) => {
if (!this.metaReady) {
throw new Error("meta metrics not ready")
@ -203,13 +204,21 @@ export default class Handler {
} */
async GetAppsMetrics(req: Types.AppsMetricsRequest): Promise<Types.AppsMetrics> {
const cached = this.appsMetricsCache.Get(req)
const now = Date.now()
if (cached && now - cached.createdAt < cacheTTL) {
return cached.metrics
}
const dbApps = await this.storage.applicationStorage.GetApplications()
const apps = await Promise.all(dbApps.map(app => this.GetAppMetrics(req, app)))
const unlinked = await this.GetAppMetrics(req, null)
apps.push(unlinked)
return {
const metrics = {
apps
}
this.appsMetricsCache.Set(req, { metrics, createdAt: now })
return metrics
}
async GetAppMetrics(req: Types.AppsMetricsRequest, app: Application | null): Promise<Types.AppMetrics> {
@ -332,6 +341,11 @@ export default class Handler {
}
async GetLndForwardingMetrics(req: Types.LndMetricsRequest): Promise<Types.LndForwardingMetrics> {
const cached = this.lndForwardingMetricsCache.Get(req)
const now = Date.now()
if (cached && now - cached.createdAt < cacheTTL) {
return cached.metrics
}
const fwEvents = await this.lnd.GetForwardingHistory(0, req.from_unix, req.to_unix)
let totalFees = 0
const events: Types.LndForwardingEvent[] = fwEvents.forwardingEvents.map(e => {
@ -340,14 +354,21 @@ export default class Handler {
chan_id_in: e.chanIdIn, chan_id_out: e.chanIdOut, amt_in: Number(e.amtIn), amt_out: Number(e.amtOut), fee: Number(e.fee), at_unix: Number(e.timestampNs)
}
})
return {
const metrics = {
total_fees: totalFees,
events: events
}
this.lndForwardingMetricsCache.Set(req, { metrics, createdAt: now })
return metrics
}
async GetLndMetrics(req: Types.LndMetricsRequest): Promise<Types.LndMetrics> {
const cached = this.lndMetricsCache.Get(req)
const now = Date.now()
if (cached && now - cached.createdAt < cacheTTL) {
return cached.metrics
}
const [chansInfo, pendingChansInfo, closedChansInfo, routing, rootOps, channelsActivity] = await Promise.all([
this.GetChannelsInfo(),
this.GetPendingChannelsInfo(),
@ -392,7 +413,7 @@ export default class Handler {
}
}))
return {
const metrics = {
nodes: [{
chain_balance: chainBalance,
channel_balance: chansBalance,
@ -408,6 +429,8 @@ export default class Handler {
root_ops: rootOps.map(r => ({ amount: r.operation_amount, created_at_unix: r.at_unix || 0, op_id: r.operation_identifier, op_type: mapRootOpType(r.operation_type) })),
}],
}
this.lndMetricsCache.Set(req, { metrics, createdAt: now })
return metrics
}
async AddRootAddressPaid(address: string, txOutput: { hash: string; index: number }, amount: number) {
@ -433,3 +456,31 @@ const mapRootOpType = (opType: string): Types.OperationType => {
default: throw new Error("Unknown operation type")
}
}
type CacheData<T> = {
metrics: T
createdAt: number
}
class CacheController<T> {
private cache: Record<string, CacheData<T>> = {}
Get = (req: Types.AppsMetricsRequest): CacheData<T> | undefined => {
const key = this.getKey(req)
return this.cache[key]
}
Set = (req: Types.AppsMetricsRequest, metrics: CacheData<T>) => {
const key = this.getKey(req)
this.cache[key] = metrics
}
Clear = (req: Types.AppsMetricsRequest) => {
const key = this.getKey(req)
delete this.cache[key]
}
private getKey = (req: Types.AppsMetricsRequest) => {
const start = req.from_unix || 0
const end = req.to_unix || 0
const includeOperations = req.include_operations ? "1" : "0"
return `${start}:${end}:${includeOperations}`
}
}

View file

@ -132,12 +132,12 @@ const handleNostrSettings = (settings: NostrSettings) => {
send(event)
})
} */
const sendToNostr: NostrSend = (initiator, data, relays) => {
const sendToNostr: NostrSend = async (initiator, data, relays) => {
if (!subProcessHandler) {
getLogger({ component: "nostrMiddleware" })(ERROR, "nostr was not initialized")
return
}
subProcessHandler.Send(initiator, data, relays)
await subProcessHandler.Send(initiator, data, relays)
}
send({ type: 'ready' })

View file

@ -1,14 +1,14 @@
import { base64 } from "@scure/base";
import { base64, hex } from "@scure/base";
import { randomBytes } from "@noble/hashes/utils";
import { streamXOR as xchacha20 } from "@stablelib/xchacha20";
import { secp256k1 } from "@noble/curves/secp256k1";
import { secp256k1 } from "@noble/curves/secp256k1.js";
import { sha256 } from "@noble/hashes/sha256";
export type EncryptedData = {
ciphertext: Uint8Array;
nonce: Uint8Array;
}
export const getSharedSecret = (privateKey: string, publicKey: string) => {
const key = secp256k1.getSharedSecret(privateKey, "02" + publicKey);
const key = secp256k1.getSharedSecret(hex.decode(privateKey), hex.decode("02" + publicKey));
return sha256(key.slice(1, 33));
}

View file

@ -16,7 +16,7 @@ export type SendDataContent = { type: "content", content: string, pub: string }
export type SendDataEvent = { type: "event", event: UnsignedEvent, encrypt?: { toPub: string } }
export type SendData = SendDataContent | SendDataEvent
export type SendInitiator = { type: 'app', appId: string } | { type: 'client', clientId: string }
export type NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => void
export type NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => Promise<void>
export type LinkedProviderInfo = { pubkey: string, clientId: string, relayUrl: string }
export type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string, provider?: LinkedProviderInfo }
@ -203,21 +203,26 @@ export class NostrPool {
const signed = finalizeEvent(event, Buffer.from(keys.privateKey, 'hex'))
let sent = false
const log = getLogger({ appName: keys.name })
// const r = relays ? relays : this.getServiceRelays()
this.log(`📤 Publishing Kind ${event.kind} event to ${relays.length} relay(s): ${relays.join(', ')}`)
const pool = new SimplePool()
try {
await Promise.all(pool.publish(relays, signed).map(async p => {
try {
await p
sent = true
} catch (e: any) {
console.log(e)
this.log(ERROR, `Failed to publish Kind ${event.kind} event:`, e.message || e)
log(e)
}
}))
if (!sent) {
this.log(ERROR, `Failed to send Kind ${event.kind} event to any relay`)
log("failed to send event")
} else {
//log("sent event")
this.log(`✅ Kind ${event.kind} event published successfully (id: ${signed.id.slice(0, 16)}...)`)
}
} finally {
pool.close(relays)
}
}

View file

@ -1,7 +1,7 @@
import { NostrSend, SendData, SendInitiator } from "./nostrPool.js"
import { getLogger } from "../helpers/logger.js"
import { ERROR, getLogger } from "../helpers/logger.js"
export class NostrSender {
private _nostrSend: NostrSend = () => { throw new Error('nostr send not initialized yet') }
private _nostrSend: NostrSend = async () => { throw new Error('nostr send not initialized yet') }
private isReady: boolean = false
private onReadyCallbacks: (() => void)[] = []
private pendingSends: { initiator: SendInitiator, data: SendData, relays?: string[] | undefined }[] = []
@ -12,7 +12,12 @@ export class NostrSender {
this.isReady = true
this.onReadyCallbacks.forEach(cb => cb())
this.onReadyCallbacks = []
this.pendingSends.forEach(send => this._nostrSend(send.initiator, send.data, send.relays))
// Process pending sends with proper error handling
this.pendingSends.forEach(send => {
this._nostrSend(send.initiator, send.data, send.relays).catch(e => {
this.log(ERROR, "failed to send pending event", e.message || e)
})
})
this.pendingSends = []
}
OnReady(callback: () => void) {
@ -22,13 +27,16 @@ export class NostrSender {
this.onReadyCallbacks.push(callback)
}
}
Send(initiator: SendInitiator, data: SendData, relays?: string[] | undefined) {
Send(initiator: SendInitiator, data: SendData, relays?: string[] | undefined): void {
if (!this.isReady) {
this.log("tried to send before nostr was ready, caching request")
this.pendingSends.push({ initiator, data, relays })
return
}
this._nostrSend(initiator, data, relays)
// Fire and forget but log errors
this._nostrSend(initiator, data, relays).catch(e => {
this.log(ERROR, "failed to send event", e.message || e)
})
}
IsReady() {
return this.isReady

View file

@ -84,7 +84,7 @@ export default class {
async execNextInQueue() {
this.pendingTx = false
const next = this.transactionsQueue.pop()
const next = this.transactionsQueue.shift()
if (!next) {
this.doneWriting()
return

View file

@ -80,8 +80,14 @@ export class InvoiceSwap {
@Column({ default: "", type: "text" })
lockup_tx_hex: string
/* @Column({ default: "" })
address_paid: string */
@Column({ default: "" })
refund_address: string
@Column({ default: "" })
refund_at_unix: number
@Column({ default: "" })
refund_tx_id: string
@Column({ default: "" })
service_url: string

View file

@ -0,0 +1,20 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class RefundSwapInfo1773082318982 implements MigrationInterface {
name = 'RefundSwapInfo1773082318982'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "temporary_invoice_swap" ("swap_operation_id" varchar PRIMARY KEY NOT NULL, "app_user_id" varchar NOT NULL, "swap_quote_id" varchar NOT NULL, "swap_tree" varchar NOT NULL, "claim_public_key" varchar NOT NULL, "payment_hash" varchar NOT NULL, "timeout_block_height" integer NOT NULL, "invoice" varchar NOT NULL, "invoice_amount" integer NOT NULL, "transaction_amount" integer NOT NULL, "swap_fee_sats" integer NOT NULL, "chain_fee_sats" integer NOT NULL, "ephemeral_public_key" varchar NOT NULL, "address" varchar NOT NULL, "ephemeral_private_key" varchar NOT NULL, "used" boolean NOT NULL DEFAULT (0), "preimage" varchar NOT NULL DEFAULT (''), "failure_reason" varchar NOT NULL DEFAULT (''), "tx_id" varchar NOT NULL DEFAULT (''), "service_url" varchar NOT NULL DEFAULT (''), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')), "lockup_tx_hex" text NOT NULL DEFAULT (''), "completed_at_unix" integer NOT NULL DEFAULT (0), "paid_at_unix" integer NOT NULL DEFAULT (0), "refund_address" varchar NOT NULL DEFAULT (''), "refund_at_unix" integer NOT NULL DEFAULT (''), "refund_tx_id" varchar NOT NULL DEFAULT (''))`);
await queryRunner.query(`INSERT INTO "temporary_invoice_swap"("swap_operation_id", "app_user_id", "swap_quote_id", "swap_tree", "claim_public_key", "payment_hash", "timeout_block_height", "invoice", "invoice_amount", "transaction_amount", "swap_fee_sats", "chain_fee_sats", "ephemeral_public_key", "address", "ephemeral_private_key", "used", "preimage", "failure_reason", "tx_id", "service_url", "created_at", "updated_at", "lockup_tx_hex", "completed_at_unix", "paid_at_unix") SELECT "swap_operation_id", "app_user_id", "swap_quote_id", "swap_tree", "claim_public_key", "payment_hash", "timeout_block_height", "invoice", "invoice_amount", "transaction_amount", "swap_fee_sats", "chain_fee_sats", "ephemeral_public_key", "address", "ephemeral_private_key", "used", "preimage", "failure_reason", "tx_id", "service_url", "created_at", "updated_at", "lockup_tx_hex", "completed_at_unix", "paid_at_unix" FROM "invoice_swap"`);
await queryRunner.query(`DROP TABLE "invoice_swap"`);
await queryRunner.query(`ALTER TABLE "temporary_invoice_swap" RENAME TO "invoice_swap"`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "invoice_swap" RENAME TO "temporary_invoice_swap"`);
await queryRunner.query(`CREATE TABLE "invoice_swap" ("swap_operation_id" varchar PRIMARY KEY NOT NULL, "app_user_id" varchar NOT NULL, "swap_quote_id" varchar NOT NULL, "swap_tree" varchar NOT NULL, "claim_public_key" varchar NOT NULL, "payment_hash" varchar NOT NULL, "timeout_block_height" integer NOT NULL, "invoice" varchar NOT NULL, "invoice_amount" integer NOT NULL, "transaction_amount" integer NOT NULL, "swap_fee_sats" integer NOT NULL, "chain_fee_sats" integer NOT NULL, "ephemeral_public_key" varchar NOT NULL, "address" varchar NOT NULL, "ephemeral_private_key" varchar NOT NULL, "used" boolean NOT NULL DEFAULT (0), "preimage" varchar NOT NULL DEFAULT (''), "failure_reason" varchar NOT NULL DEFAULT (''), "tx_id" varchar NOT NULL DEFAULT (''), "service_url" varchar NOT NULL DEFAULT (''), "created_at" datetime NOT NULL DEFAULT (datetime('now')), "updated_at" datetime NOT NULL DEFAULT (datetime('now')), "lockup_tx_hex" text NOT NULL DEFAULT (''), "completed_at_unix" integer NOT NULL DEFAULT (0), "paid_at_unix" integer NOT NULL DEFAULT (0))`);
await queryRunner.query(`INSERT INTO "invoice_swap"("swap_operation_id", "app_user_id", "swap_quote_id", "swap_tree", "claim_public_key", "payment_hash", "timeout_block_height", "invoice", "invoice_amount", "transaction_amount", "swap_fee_sats", "chain_fee_sats", "ephemeral_public_key", "address", "ephemeral_private_key", "used", "preimage", "failure_reason", "tx_id", "service_url", "created_at", "updated_at", "lockup_tx_hex", "completed_at_unix", "paid_at_unix") SELECT "swap_operation_id", "app_user_id", "swap_quote_id", "swap_tree", "claim_public_key", "payment_hash", "timeout_block_height", "invoice", "invoice_amount", "transaction_amount", "swap_fee_sats", "chain_fee_sats", "ephemeral_public_key", "address", "ephemeral_private_key", "used", "preimage", "failure_reason", "tx_id", "service_url", "created_at", "updated_at", "lockup_tx_hex", "completed_at_unix", "paid_at_unix" FROM "temporary_invoice_swap"`);
await queryRunner.query(`DROP TABLE "temporary_invoice_swap"`);
}
}

View file

@ -30,6 +30,7 @@ import { InvoiceSwapsFixes1769805357459 } from './1769805357459-invoice_swaps_fi
import { ApplicationUserTopicId1770038768784 } from './1770038768784-application_user_topic_id.js'
import { SwapTimestamps1771347307798 } from './1771347307798-swap_timestamps.js'
import { TxSwapTimestamps1771878683383 } from './1771878683383-tx_swap_timestamps.js'
import { RefundSwapInfo1773082318982 } from './1773082318982-refund_swap_info.js'
import { LndMetrics1703170330183 } from './1703170330183-lnd_metrics.js'
import { ChannelRouting1709316653538 } from './1709316653538-channel_routing.js'
@ -51,7 +52,7 @@ export const allMigrations = [Initial1703170309875, LspOrder1718387847693, Liqui
UserAccess1759426050669, AddBlindToUserOffer1760000000000, ApplicationAvatarUrl1761000001000, AdminSettings1761683639419, TxSwap1762890527098,
TxSwapAddress1764779178945, ClinkRequester1765497600000, TrackedProviderHeight1766504040000, SwapsServiceUrl1768413055036,
InvoiceSwaps1769529793283, InvoiceSwapsFixes1769805357459, ApplicationUserTopicId1770038768784, SwapTimestamps1771347307798,
TxSwapTimestamps1771878683383]
TxSwapTimestamps1771878683383, RefundSwapInfo1773082318982]
export const allMetricsMigrations = [LndMetrics1703170330183, ChannelRouting1709316653538, HtlcCount1724266887195, BalanceEvents1724860966825,

View file

@ -655,6 +655,15 @@ export default class {
return swaps.filter(s => !!s.tx_id)
}
async UpdateRefundInvoiceSwap(swapOperationId: string, refundAddress: string, refundTxId: string, txId?: string) {
const now = Math.floor(Date.now() / 1000)
return this.dbs.Update<InvoiceSwap>('InvoiceSwap', { swap_operation_id: swapOperationId }, {
refund_address: refundAddress,
refund_at_unix: now,
refund_tx_id: refundTxId,
}, txId)
}
async GetRefundableInvoiceSwap(swapOperationId: string, txId?: string) {
const swap = await this.dbs.FindOne<InvoiceSwap>('InvoiceSwap', { where: { swap_operation_id: swapOperationId } }, txId)
if (!swap || !swap.tx_id) {

View file

@ -126,7 +126,7 @@ class TlvFilesStorageProcessor {
throw new Error('Unknown metric type: ' + t)
}
})
this.wrtc.attachNostrSend((initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => {
this.wrtc.attachNostrSend(async (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => {
this.sendResponse({
success: true,
type: 'nostrSend',

View file

@ -27,11 +27,11 @@ export default class webRTC {
attachNostrSend(f: NostrSend) {
this._nostrSend = f
}
private nostrSend: NostrSend = (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => {
private nostrSend: NostrSend = async (initiator: SendInitiator, data: SendData, relays?: string[] | undefined) => {
if (!this._nostrSend) {
throw new Error("No nostrSend attached")
}
this._nostrSend(initiator, data, relays)
await this._nostrSend(initiator, data, relays)
}
private sendCandidate = (u: WebRtcUserInfo, candidate: string) => {