Skip to content

Commit 24e6593

Browse files
committed
add support for HODL invoices
1 parent fa667a6 commit 24e6593

11 files changed

Lines changed: 1769 additions & 68 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ The node currently exposes the following APIs:
203203
- `/assetmetadata` (POST)
204204
- `/backup` (POST)
205205
- `/btcbalance` (POST)
206+
- `/cancelhodlinvoice` (POST)
206207
- `/changepassword` (POST)
207208
- `/checkindexerurl` (POST)
208209
- `/checkproxyendpoint` (POST)
@@ -217,7 +218,9 @@ The node currently exposes the following APIs:
217218
- `/getassetmedia` (POST)
218219
- `/getchannelid` (POST)
219220
- `/getpayment` (POST)
221+
- `/getpaymentpreimage` (POST)
220222
- `/getswap` (POST)
223+
- `/hodlinvoice` (POST)
221224
- `/init` (POST)
222225
- `/invoicestatus` (POST)
223226
- `/issueassetcfa` (POST)
@@ -248,6 +251,7 @@ The node currently exposes the following APIs:
248251
- `/sendonionmessage` (POST)
249252
- `/sendpayment` (POST)
250253
- `/sendrgb` (POST)
254+
- `/settlehodlinvoice` (POST)
251255
- `/shutdown` (POST)
252256
- `/signmessage` (POST)
253257
- `/sync` (POST)

openapi.yaml

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,24 @@ paths:
115115
application/json:
116116
schema:
117117
$ref: '#/components/schemas/BtcBalanceResponse'
118+
/cancelhodlinvoice:
119+
post:
120+
tags:
121+
- Invoices
122+
summary: Cancel a HODL invoice
123+
description: Cancel a held HTLC for a HODL invoice. Rejects cancellation if a settlement is already in progress.
124+
requestBody:
125+
content:
126+
application/json:
127+
schema:
128+
$ref: '#/components/schemas/InvoiceCancelRequest'
129+
responses:
130+
'200':
131+
description: Successful operation
132+
content:
133+
application/json:
134+
schema:
135+
$ref: '#/components/schemas/EmptyResponse'
118136
/changepassword:
119137
post:
120138
tags:
@@ -368,6 +386,24 @@ paths:
368386
application/json:
369387
schema:
370388
$ref: '#/components/schemas/GetPaymentResponse'
389+
/getpaymentpreimage:
390+
post:
391+
tags:
392+
- Payments
393+
summary: Get a payment preimage by its payment hash
394+
description: Get the preimage for an outbound payment when it has been completed successfully
395+
requestBody:
396+
content:
397+
application/json:
398+
schema:
399+
$ref: '#/components/schemas/GetPaymentPreimageRequest'
400+
responses:
401+
'200':
402+
description: Successful operation
403+
content:
404+
application/json:
405+
schema:
406+
$ref: '#/components/schemas/GetPaymentPreimageResponse'
371407
/getswap:
372408
post:
373409
tags:
@@ -386,6 +422,24 @@ paths:
386422
application/json:
387423
schema:
388424
$ref: '#/components/schemas/GetSwapResponse'
425+
/hodlinvoice:
426+
post:
427+
tags:
428+
- Invoices
429+
summary: Create a HODL LN invoice
430+
description: Create a BOLT11 invoice with a caller-provided payment hash; settlement is deferred until settle/cancel. Metadata is persisted first to preserve HODL semantics across restarts.
431+
requestBody:
432+
content:
433+
application/json:
434+
schema:
435+
$ref: '#/components/schemas/InvoiceHodlRequest'
436+
responses:
437+
'200':
438+
description: Successful operation
439+
content:
440+
application/json:
441+
schema:
442+
$ref: '#/components/schemas/InvoiceHodlResponse'
389443
/init:
390444
post:
391445
tags:
@@ -892,6 +946,24 @@ paths:
892946
application/json:
893947
schema:
894948
$ref: '#/components/schemas/SendRgbResponse'
949+
/settlehodlinvoice:
950+
post:
951+
tags:
952+
- Invoices
953+
summary: Settle a HODL invoice
954+
description: Claim a held HTLC for a HODL invoice
955+
requestBody:
956+
content:
957+
application/json:
958+
schema:
959+
$ref: '#/components/schemas/SettleHodlInvoiceRequest'
960+
responses:
961+
'200':
962+
description: Successful operation
963+
content:
964+
application/json:
965+
schema:
966+
$ref: '#/components/schemas/SettleHodlInvoiceResponse'
895967
/shutdown:
896968
post:
897969
tags:
@@ -1245,6 +1317,14 @@ components:
12451317
$ref: '#/components/schemas/BtcBalance'
12461318
colored:
12471319
$ref: '#/components/schemas/BtcBalance'
1320+
CancelHodlInvoiceRequest:
1321+
type: object
1322+
required:
1323+
- payment_hash
1324+
properties:
1325+
payment_hash:
1326+
type: string
1327+
example: 3febfae1e68b190c15461f4c2a3290f9af1dae63fd7d620d2bd61601869026cd
12481328
ChangePasswordRequest:
12491329
type: object
12501330
properties:
@@ -1330,7 +1410,7 @@ components:
13301410
CheckProxyEndpointRequest:
13311411
type: object
13321412
properties:
1333-
proxy_url:
1413+
proxy_endpoint:
13341414
type: string
13351415
example: rpc://127.0.0.1:3000/json-rpc
13361416
CloseChannelRequest:
@@ -1519,6 +1599,23 @@ components:
15191599
properties:
15201600
payment:
15211601
$ref: '#/components/schemas/Payment'
1602+
GetPaymentPreimageRequest:
1603+
type: object
1604+
required:
1605+
- payment_hash
1606+
properties:
1607+
payment_hash:
1608+
type: string
1609+
example: b4cb2da889477082a2e47f37a07e646e60ef6f97ffa7a4d88c823efd673da94b
1610+
GetPaymentPreimageResponse:
1611+
type: object
1612+
properties:
1613+
status:
1614+
$ref: '#/components/schemas/HTLCStatus'
1615+
preimage:
1616+
type: string
1617+
nullable: true
1618+
example: eade701c7b23b8799465f4284ad84710fc16a776fbc6483001291149122695a8
15221619
GetSwapRequest:
15231620
type: object
15241621
properties:
@@ -1537,7 +1634,9 @@ components:
15371634
type: string
15381635
enum:
15391636
- Pending
1637+
- Claimable
15401638
- Succeeded
1639+
- Cancelled
15411640
- Failed
15421641
IndexerProtocol:
15431642
type: string
@@ -1556,11 +1655,45 @@ components:
15561655
mnemonic:
15571656
type: string
15581657
example: skill lamp please gown put season degree collect decline account monitor insane
1658+
InvoiceHodlRequest:
1659+
type: object
1660+
required:
1661+
- payment_hash
1662+
- expiry_sec
1663+
properties:
1664+
amt_msat:
1665+
type: integer
1666+
example: 3000000
1667+
expiry_sec:
1668+
type: integer
1669+
example: 86400
1670+
asset_id:
1671+
type: string
1672+
example: rgb:CJkb4YZw-jRiz2sk-~PARPio-wtVYI1c-XAEYCqO-wTfvRZ8
1673+
asset_amount:
1674+
type: integer
1675+
example: 42
1676+
payment_hash:
1677+
type: string
1678+
example: 3febfae1e68b190c15461f4c2a3290f9af1dae63fd7d620d2bd61601869026cd
1679+
external_ref:
1680+
type: string
1681+
example: swap-123
1682+
InvoiceHodlResponse:
1683+
type: object
1684+
properties:
1685+
invoice:
1686+
type: string
1687+
example: lnbcrt30u1pjv6yzndqud3jxktt5w46x7unfv9kz6mn0v3jsnp4qdpc280eur52luxppv6f3nnj8l6vnd9g2hnv3qv6mjhmhvlzf6327pp5tjjasx6g9dqptea3fhm6yllq5wxzycnnvp8l6wcq3d6j2uvpryuqsp5l8az8x3g8fe05dg7cmgddld3da09nfjvky8xftwsk4cj8p2l7kfq9qyysgqcqpcxqzdylzlwfnkyw3jv344x4rzwgkk53ng0fhxy5rdduk4g5tpvea8xa6rfckkza35va28xjn2tqkhgarcxep5umm4x5k56wfcdvu95eq7qzp20vrl4xz76syapsa3c09j7lg5gerkaj63llj0ark7ph8hfketn6fkqzm8laf66dhsncm23wkwm5l5377we9e8lnlknnkwje5eefkccusqm6rqt8
1688+
payment_secret:
1689+
type: string
1690+
example: 777a7756c620868199ed5fdc35bee4095b5709d543e5c2bf0494396bf27d2ea2
15591691
InvoiceStatus:
15601692
type: string
15611693
enum:
15621694
- Pending
15631695
- Succeeded
1696+
- Cancelled
15641697
- Failed
15651698
- Expired
15661699
InvoiceStatusRequest:
@@ -2184,6 +2317,24 @@ components:
21842317
txid:
21852318
type: string
21862319
example: 7c2c95b9c2aa0a7d140495b664de7973b76561de833f0dd84def3efa08941664
2320+
SettleHodlInvoiceRequest:
2321+
type: object
2322+
required:
2323+
- payment_hash
2324+
- payment_preimage
2325+
properties:
2326+
payment_hash:
2327+
type: string
2328+
example: b4cb2da889477082a2e47f37a07e646e60ef6f97ffa7a4d88c823efd673da94b
2329+
payment_preimage:
2330+
type: string
2331+
example: eade701c7b23b8799465f4284ad84710fc16a776fbc6483001291149122695a8
2332+
SettleHodlInvoiceResponse:
2333+
type: object
2334+
properties:
2335+
changed:
2336+
type: boolean
2337+
example: true
21872338
SignMessageRequest:
21882339
type: object
21892340
properties:

regtest.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ _is_port_bound() {
3838
_wait_for_bitcoind() {
3939
# wait for bitcoind to be up
4040
start_time=$(date +%s)
41-
until $COMPOSE logs bitcoind |grep -q 'Bound to'; do
41+
until $BITCOIN_CLI getblockcount >/dev/null 2>&1; do
4242
current_time=$(date +%s)
4343
if [ $((current_time - start_time)) -gt $TIMEOUT ]; then
4444
echo "Timeout waiting for bitcoind to start"
@@ -74,9 +74,10 @@ _start_services() {
7474
_die "port $port is already bound, services can't be started"
7575
fi
7676
done
77-
$COMPOSE up -d
77+
$COMPOSE up -d bitcoind
7878
echo && echo "preparing bitcoind wallet"
7979
_wait_for_bitcoind
80+
$COMPOSE up -d electrs proxy
8081
$BITCOIN_CLI createwallet miner >/dev/null
8182
$BITCOIN_CLI -rpcwallet=miner -generate $INITIAL_BLOCKS >/dev/null
8283
echo "waiting for electrs to have completed startup"

src/disk.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@ use std::sync::Arc;
1515

1616
use crate::error::APIError;
1717
use crate::ldk::{
18-
ChannelIdsMap, InboundPaymentInfoStorage, NetworkGraph, OutboundPaymentInfoStorage,
19-
OutputSpenderTxes, SwapMap,
18+
ChannelIdsMap, ClaimablePaymentStorage, InboundPaymentInfoStorage, InvoiceMetadataStorage,
19+
NetworkGraph, OutboundPaymentInfoStorage, OutputSpenderTxes, SwapMap,
2020
};
2121
use crate::utils::{parse_peer_info, LOGS_DIR};
2222

2323
pub(crate) const LDK_LOGS_FILE: &str = "logs.txt";
2424

2525
pub(crate) const INBOUND_PAYMENTS_FNAME: &str = "inbound_payments";
2626
pub(crate) const OUTBOUND_PAYMENTS_FNAME: &str = "outbound_payments";
27+
pub(crate) const INVOICE_METADATA_FNAME: &str = "invoice_metadata";
28+
pub(crate) const CLAIMABLE_HTLCS_FNAME: &str = "claimable_htlcs";
2729

2830
pub(crate) const CHANNEL_PEER_DATA: &str = "channel_peer_data";
2931

@@ -178,6 +180,28 @@ pub(crate) fn read_outbound_payment_info(path: &Path) -> OutboundPaymentInfoStor
178180
}
179181
}
180182

183+
pub(crate) fn read_invoice_metadata(path: &Path) -> InvoiceMetadataStorage {
184+
if let Ok(file) = File::open(path) {
185+
if let Ok(info) = InvoiceMetadataStorage::read(&mut BufReader::new(file)) {
186+
return info;
187+
}
188+
}
189+
InvoiceMetadataStorage {
190+
invoices: new_hash_map(),
191+
}
192+
}
193+
194+
pub(crate) fn read_claimable_htlcs(path: &Path) -> ClaimablePaymentStorage {
195+
if let Ok(file) = File::open(path) {
196+
if let Ok(info) = ClaimablePaymentStorage::read(&mut BufReader::new(file)) {
197+
return info;
198+
}
199+
}
200+
ClaimablePaymentStorage {
201+
payments: new_hash_map(),
202+
}
203+
}
204+
181205
pub(crate) fn read_output_spender_txes(path: &Path) -> OutputSpenderTxes {
182206
if let Ok(file) = File::open(path) {
183207
if let Ok(info) = OutputSpenderTxes::read(&mut BufReader::new(file)) {

0 commit comments

Comments
 (0)