Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ The node currently exposes the following APIs:
- `/assetmetadata` (POST)
- `/backup` (POST)
- `/btcbalance` (POST)
- `/cancelhodlinvoice` (POST)
- `/changepassword` (POST)
- `/checkindexerurl` (POST)
- `/checkproxyendpoint` (POST)
Expand All @@ -217,7 +218,9 @@ The node currently exposes the following APIs:
- `/getassetmedia` (POST)
- `/getchannelid` (POST)
- `/getpayment` (POST)
- `/getpaymentpreimage` (POST)
- `/getswap` (POST)
- `/hodlinvoice` (POST)
- `/init` (POST)
- `/invoicestatus` (POST)
- `/issueassetcfa` (POST)
Expand Down Expand Up @@ -248,6 +251,7 @@ The node currently exposes the following APIs:
- `/sendbtc` (POST)
- `/sendonionmessage` (POST)
- `/sendpayment` (POST)
- `/settlehodlinvoice` (POST)
- `/shutdown` (POST)
- `/signmessage` (POST)
- `/sync` (POST)
Expand Down
145 changes: 145 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/BtcBalanceResponse'
/cancelhodlinvoice:
post:
tags:
- Invoices
summary: Cancel a HODL invoice
description: Cancel a held HTLC for a HODL invoice. Rejects cancellation if a settlement is already in progress.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/InvoiceCancelRequest'
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/EmptyResponse'
/changepassword:
post:
tags:
Expand Down Expand Up @@ -368,6 +386,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GetPaymentResponse'
/getpaymentpreimage:
post:
tags:
- Payments
summary: Get a payment preimage by its payment hash
description: Get the preimage for an outbound payment when it has been completed successfully
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/GetPaymentPreimageRequest'
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/GetPaymentPreimageResponse'
/getswap:
post:
tags:
Expand All @@ -386,6 +422,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GetSwapResponse'
/hodlinvoice:
post:
tags:
- Invoices
summary: Create a HODL LN invoice
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.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/InvoiceHodlRequest'
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/InvoiceHodlResponse'
/init:
post:
tags:
Expand Down Expand Up @@ -892,6 +946,24 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SendPaymentResponse'
/settlehodlinvoice:
post:
tags:
- Invoices
summary: Settle a HODL invoice
description: Claim a held HTLC for a HODL invoice
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/InvoiceSettleRequest'
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/EmptyResponse'
/shutdown:
post:
tags:
Expand Down Expand Up @@ -1519,6 +1591,23 @@ components:
properties:
payment:
$ref: '#/components/schemas/Payment'
GetPaymentPreimageRequest:
type: object
required:
- payment_hash
properties:
payment_hash:
type: string
example: b4cb2da889477082a2e47f37a07e646e60ef6f97ffa7a4d88c823efd673da94b
GetPaymentPreimageResponse:
type: object
properties:
status:
$ref: '#/components/schemas/HTLCStatus'
preimage:
type: string
nullable: true
example: eade701c7b23b8799465f4284ad84710fc16a776fbc6483001291149122695a8
GetSwapRequest:
type: object
properties:
Expand All @@ -1537,7 +1626,9 @@ components:
type: string
enum:
- Pending
- Claimable
- Succeeded
- Cancelled
- Failed
IndexerProtocol:
type: string
Expand All @@ -1556,11 +1647,65 @@ components:
mnemonic:
type: string
example: skill lamp please gown put season degree collect decline account monitor insane
InvoiceCancelRequest:
type: object
required:
- payment_hash
properties:
payment_hash:
type: string
example: 3febfae1e68b190c15461f4c2a3290f9af1dae63fd7d620d2bd61601869026cd
InvoiceHodlRequest:
type: object
required:
- payment_hash
- expiry_sec
properties:
amt_msat:
type: integer
example: 3000000
expiry_sec:
type: integer
example: 86400
asset_id:
type: string
example: rgb:CJkb4YZw-jRiz2sk-~PARPio-wtVYI1c-XAEYCqO-wTfvRZ8
asset_amount:
type: integer
example: 42
payment_hash:
type: string
example: 3febfae1e68b190c15461f4c2a3290f9af1dae63fd7d620d2bd61601869026cd
external_ref:
type: string
example: swap-123
InvoiceHodlResponse:
type: object
properties:
invoice:
type: string
example: lnbcrt30u1pjv6yzndqud3jxktt5w46x7unfv9kz6mn0v3jsnp4qdpc280eur52luxppv6f3nnj8l6vnd9g2hnv3qv6mjhmhvlzf6327pp5tjjasx6g9dqptea3fhm6yllq5wxzycnnvp8l6wcq3d6j2uvpryuqsp5l8az8x3g8fe05dg7cmgddld3da09nfjvky8xftwsk4cj8p2l7kfq9qyysgqcqpcxqzdylzlwfnkyw3jv344x4rzwgkk53ng0fhxy5rdduk4g5tpvea8xa6rfckkza35va28xjn2tqkhgarcxep5umm4x5k56wfcdvu95eq7qzp20vrl4xz76syapsa3c09j7lg5gerkaj63llj0ark7ph8hfketn6fkqzm8laf66dhsncm23wkwm5l5377we9e8lnlknnkwje5eefkccusqm6rqt8
payment_secret:
type: string
example: 777a7756c620868199ed5fdc35bee4095b5709d543e5c2bf0494396bf27d2ea2
InvoiceSettleRequest:
type: object
required:
- payment_hash
- payment_preimage
properties:
payment_hash:
type: string
example: b4cb2da889477082a2e47f37a07e646e60ef6f97ffa7a4d88c823efd673da94b
payment_preimage:
type: string
example: eade701c7b23b8799465f4284ad84710fc16a776fbc6483001291149122695a8
InvoiceStatus:
type: string
enum:
- Pending
- Succeeded
- Cancelled
- Failed
- Expired
InvoiceStatusRequest:
Expand Down
18 changes: 17 additions & 1 deletion regtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ _wait_for_bitcoind() {
done
}

_wait_for_bitcoind_rpc() {
# wait for RPC to accept requests
start_time=$(date +%s)
until $BITCOIN_CLI getblockcount >/dev/null 2>&1; do
current_time=$(date +%s)
if [ $((current_time - start_time)) -gt $TIMEOUT ]; then
echo "Timeout waiting for bitcoind RPC to start"
$COMPOSE logs bitcoind
exit 1
fi
sleep 1
done
}

_wait_for_electrs() {
# wait for electrs to have completed startup
start_time=$(date +%s)
Expand All @@ -74,9 +88,11 @@ _start_services() {
_die "port $port is already bound, services can't be started"
fi
done
$COMPOSE up -d
$COMPOSE up -d bitcoind
echo && echo "preparing bitcoind wallet"
_wait_for_bitcoind
_wait_for_bitcoind_rpc
$COMPOSE up -d electrs proxy
$BITCOIN_CLI createwallet miner >/dev/null
$BITCOIN_CLI -rpcwallet=miner -generate $INITIAL_BLOCKS >/dev/null
echo "waiting for electrs to have completed startup"
Expand Down
28 changes: 26 additions & 2 deletions src/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@ use std::sync::Arc;

use crate::error::APIError;
use crate::ldk::{
ChannelIdsMap, InboundPaymentInfoStorage, NetworkGraph, OutboundPaymentInfoStorage,
OutputSpenderTxes, SwapMap,
ChannelIdsMap, ClaimablePaymentStorage, InboundPaymentInfoStorage, InvoiceMetadataStorage,
NetworkGraph, OutboundPaymentInfoStorage, OutputSpenderTxes, SwapMap,
};
use crate::utils::{parse_peer_info, LOGS_DIR};

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

pub(crate) const INBOUND_PAYMENTS_FNAME: &str = "inbound_payments";
pub(crate) const OUTBOUND_PAYMENTS_FNAME: &str = "outbound_payments";
pub(crate) const INVOICE_METADATA_FNAME: &str = "invoice_metadata";
pub(crate) const CLAIMABLE_HTLCS_FNAME: &str = "claimable_htlcs";

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

Expand Down Expand Up @@ -178,6 +180,28 @@ pub(crate) fn read_outbound_payment_info(path: &Path) -> OutboundPaymentInfoStor
}
}

pub(crate) fn read_invoice_metadata(path: &Path) -> InvoiceMetadataStorage {
if let Ok(file) = File::open(path) {
if let Ok(info) = InvoiceMetadataStorage::read(&mut BufReader::new(file)) {
return info;
}
}
InvoiceMetadataStorage {
invoices: new_hash_map(),
}
}

pub(crate) fn read_claimable_htlcs(path: &Path) -> ClaimablePaymentStorage {
if let Ok(file) = File::open(path) {
if let Ok(info) = ClaimablePaymentStorage::read(&mut BufReader::new(file)) {
return info;
}
}
ClaimablePaymentStorage {
payments: new_hash_map(),
}
}

pub(crate) fn read_output_spender_txes(path: &Path) -> OutputSpenderTxes {
if let Ok(file) = File::open(path) {
if let Ok(info) = OutputSpenderTxes::read(&mut BufReader::new(file)) {
Expand Down
35 changes: 35 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ pub enum APIError {
#[error("Invalid payment hash: {0}")]
InvalidPaymentHash(String),

#[error("Payment hash already used")]
PaymentHashAlreadyUsed,

#[error("Invalid payment secret")]
InvalidPaymentSecret,

Expand Down Expand Up @@ -210,6 +213,24 @@ pub enum APIError {
#[error("Invalid transport endpoints: {0}")]
InvalidTransportEndpoints(String),

#[error("Invoice is expired")]
InvoiceExpired,

#[error("HTLC claim deadline exceeded")]
ClaimDeadlineExceeded,

#[error("Invoice is not marked as HODL")]
InvoiceNotHodl,

#[error("No claimable HTLC found for this invoice")]
InvoiceNotClaimable,

#[error("Invoice settlement is in progress")]
InvoiceSettlingInProgress,

#[error("Invoice is already settled")]
InvoiceAlreadySettled,

#[error("IO error: {0}")]
IO(#[from] std::io::Error),

Expand All @@ -234,6 +255,9 @@ pub enum APIError {
#[error("Unable to find payment preimage, be sure you've provided the correct swap info")]
MissingSwapPaymentPreimage,

#[error("Invalid payment preimage")]
InvalidPaymentPreimage,

#[error("Network error: {0}")]
Network(String),

Expand Down Expand Up @@ -437,13 +461,16 @@ impl IntoResponse for APIError {
| APIError::InvalidOnionData(_)
| APIError::InvalidPassword(_)
| APIError::InvalidPaymentHash(_)
| APIError::PaymentHashAlreadyUsed
| APIError::InvalidPaymentSecret
| APIError::InvalidPaymentPreimage
| APIError::InvalidPeerInfo(_)
| APIError::InvalidPrecision(_)
| APIError::InvalidPubkey
| APIError::InvalidRecipientData(_)
| APIError::InvalidRecipientID
| APIError::InvalidRecipientNetwork
| APIError::InvoiceExpired
| APIError::InvalidSwap(_)
| APIError::InvalidSwapString(_, _)
| APIError::InvalidTicker(_)
Expand All @@ -454,6 +481,7 @@ impl IntoResponse for APIError {
| APIError::MediaFileNotProvided
| APIError::MissingSwapPaymentPreimage
| APIError::OutputBelowDustLimit
| APIError::ClaimDeadlineExceeded
| APIError::UnsupportedBackupVersion { .. } => {
(StatusCode::BAD_REQUEST, self.to_string(), self.name())
}
Expand Down Expand Up @@ -499,6 +527,13 @@ impl IntoResponse for APIError {
| APIError::UnsupportedTransportType => {
(StatusCode::FORBIDDEN, self.to_string(), self.name())
}
APIError::InvoiceNotClaimable => (StatusCode::NOT_FOUND, self.to_string(), self.name()),
APIError::InvoiceAlreadySettled => {
(StatusCode::CONFLICT, self.to_string(), self.name())
}
APIError::InvoiceNotHodl | APIError::InvoiceSettlingInProgress => {
(StatusCode::FORBIDDEN, self.to_string(), self.name())
}
APIError::Network(_) | APIError::NoValidTransportEndpoint => (
StatusCode::SERVICE_UNAVAILABLE,
self.to_string(),
Expand Down
Loading