lightning
Version:
Lightning Network client library
219 lines (186 loc) • 7.84 kB
JavaScript
const {chanFormat} = require('bolt07');
const {htlcTypes} = require('./constants');
const {safeTokens} = require('./../bolt00');
const bufferAsHex = buffer => buffer.toString('hex');
const nsPerMs = BigInt(1e6);
/** Get RPC HTLC Event as a forward event
{
event_type: <Event Type String>
[forward_event]: {
info: {
incoming_amt_msat: <Incoming Channel HTLC Millitokens String>
incoming_timelock: <Incoming Channel CLTV Block Height Number>
outgoing_amt_msat: <Outgoing Channel HTLC Millitokens String>
outgoing_timelock: <Outgoing Channel CLTV Block Height Number>
}
}
[forward_fail_event]: <Forward Failure Event Object>
incoming_channel_id: <Inbound Channel Numeric Id String>
incoming_htlc_id: <Inbound Channel Historic Index Number>
[link_fail_event]: {
failure_detail: <Failure Code String>
failure_string: <Failure Description String>
info: {
incoming_amt_msat: <Incoming Channel HTLC Millitokens String>
incoming_timelock: <Incoming Channel CLTV Block Height Number>
outgoing_amt_msat: <Outgoing Channel HTLC Millitokens String>
outgoing_timelock: <Outgoing Channel CLTV Block Height Number>
}
wire_failure: <Wire Code String>
}
outgoing_channel_id: <Outbound Channel Numeric Id String>
outgoing_htlc_id: <Outbound Channel Historic Index Number>
[settle_event]: {
preimage: <HTLC Preimage Buffer Object>
}
timestamp_ns: <Timestamp Nanoseconds String>
}
@throws
<Error>
@returns
{
at: <Forward Update At ISO 8601 Date String>
[cltv_delta]: <CLTV Delta Number>
[external_failure]: <Public Failure Reason String>
[fee]: <Fee Tokens Charged Number>
[fee_mtokens]: <Fee Millitokens Charged String>
[in_channel]: <Inbound Standard Format Channel Id String>
[in_payment]: <Inbound Channel Payment Id Number>
[internal_failure]: <Private Failure Reason String>
is_confirmed: <Forward Is Confirmed Bool>
is_failed: <Forward Is Failed Bool>
is_receive: <Is Receive Bool>
is_send: <Is Send Bool>
[mtokens]: <Sending Millitokens String>
[out_channel]: <Outgoing Standard Format Channel Id String>
[out_payment]: <Outgoing Channel Payment Id Number>
[secret]: <Settled Preimage Hex String>
[timeout]: <Forward Timeout at Height Number>
[tokens]: <Sending Tokens Number>
}
*/
module.exports = htlc => {
if (!htlc.event_type) {
throw new Error('ExpectedHtlcEventTypeToDeriveForwardFromHtlcEvent');
}
if (!!htlc.forward_event) {
if (!htlc.forward_event.info) {
throw new Error('ExpectedHtlcInfoInForwardEvent');
}
if (!htlc.forward_event.info.incoming_amt_msat) {
throw new Error('ExpectedForwardEventIncomingAmountMsatToDeriveForward');
}
if (htlc.forward_event.info.incoming_timelock === undefined) {
throw new Error('ExpectedForwardEventIncomingTimelockToDeriveForward');
}
if (!htlc.forward_event.info.outgoing_amt_msat) {
throw new Error('ExpectedForwardEventOutgoingAmountMsatToDeriveForward');
}
if (htlc.forward_event.info.outgoing_timelock === undefined) {
throw new Error('ExpectedForwardEventOutgoingTimelockToDeriveForward');
}
}
if (!htlc.incoming_channel_id) {
throw new Error('ExpectedIncomingChannelIdToDeriveForward');
}
if (!htlc.incoming_htlc_id) {
throw new Error('ExpectedIncomingHtlcIdToDeriveForward');
}
if (!!htlc.link_fail_event) {
if (!htlc.link_fail_event.failure_detail) {
throw new Error('ExpectedLinkFailureDetailInLinkFailureEvent');
}
if (!htlc.link_fail_event.failure_string) {
throw new Error('ExpectedLinkFailureStringInLinkFailureEvent');
}
if (!htlc.link_fail_event.info) {
throw new Error('ExpectedHtlcInfoInLinkFailEvent');
}
if (!htlc.link_fail_event.info.incoming_amt_msat) {
throw new Error('ExpectedLinkFailEventIncomingMsatToDeriveForward');
}
if (htlc.link_fail_event.info.incoming_timelock === undefined) {
throw new Error('ExpectedLinkFailEventIncomingTimelockToDeriveForward');
}
if (!htlc.link_fail_event.info.outgoing_amt_msat) {
throw new Error('ExpectedLinkFailEventOutgoingMsatToDeriveForward');
}
if (htlc.link_fail_event.info.outgoing_timelock === undefined) {
throw new Error('ExpectedForwardEventOutgoingTimelockToDeriveForward');
}
}
if (!htlc.outgoing_channel_id) {
throw new Error('ExpectedOutgoingChannelIdToDeriveForward');
}
if (!htlc.outgoing_htlc_id) {
throw new Error('ExpectedOutgoingHtlcIdToDeriveForward');
}
if (!!htlc.settle_event && !Buffer.isBuffer(htlc.settle_event.preimage)) {
throw new Error('ExpectedHtlcPreimageInForwardedSettleEvent');
}
if (!htlc.timestamp_ns) {
throw new Error('ExpectedHtlcTimestampToDeriveForward');
}
const createdAt = new Date(Number(BigInt(htlc.timestamp_ns) / nsPerMs));
const incoming = chanFormat({number: htlc.incoming_channel_id});
const incomingId = Number(htlc.incoming_htlc_id);
const isFailure = !!htlc.forward_fail_event || !!htlc.link_fail_event;
const isForward = htlc.event_type === htlcTypes.forward;
const isReceive = htlc.event_type === htlcTypes.receive;
const isSend = htlc.event_type === htlcTypes.send;
const outgoing = chanFormat({number: htlc.outgoing_channel_id});
const outgoingId = Number(htlc.outgoing_htlc_id);
const settleEvent = htlc.settle_event || {preimage: Buffer.alloc(Number())};
// Exit early when there is no extended info, for failed forwards and settles
if (!!htlc.forward_fail_event || !!htlc.settle_event) {
return {
at: createdAt.toISOString(),
cltv_delta: undefined,
external_failure: undefined,
fee: undefined,
fee_mtokens: undefined,
in_channel: !!isSend ? undefined : incoming.channel,
in_payment: !!isSend ? undefined : incomingId,
internal_failure: undefined,
is_confirmed: !!htlc.settle_event,
is_failed: isFailure,
is_receive: isReceive,
is_send: isSend,
mtokens: undefined,
out_channel: !!isReceive ? undefined : outgoing.channel,
out_payment: !!isReceive ? undefined : outgoingId,
secret: bufferAsHex(settleEvent.preimage) || undefined,
timeout: undefined,
tokens: undefined,
};
}
if (!htlc.forward_event && !htlc.link_fail_event) {
throw new Error('ExpectedForwardEventOrLinkFailEventInHtlcEvent');
}
const {info} = htlc.forward_event || htlc.link_fail_event;
const outgoingAmount = BigInt(info.outgoing_amt_msat);
const outgoingCltvHeight = Number(info.outgoing_timelock);
const cltvDelta = Number(info.incoming_timelock) - outgoingCltvHeight;
const feeMtok = (BigInt(info.incoming_amt_msat) - outgoingAmount).toString();
const {tokens} = safeTokens({mtokens: info.outgoing_amt_msat});
return {
at: createdAt.toISOString(),
cltv_delta: !isForward ? undefined : cltvDelta,
external_failure: (htlc.link_fail_event || {}).wire_failure,
fee: !isForward ? undefined : safeTokens({mtokens: feeMtok}).tokens,
fee_mtokens: !isForward ? undefined : feeMtok,
in_channel: !!isSend ? undefined : incoming.channel,
in_payment: !!isSend ? undefined : incomingId,
internal_failure: (htlc.link_fail_event || {}).failure_detail,
is_confirmed: false,
is_failed: !!htlc.link_fail_event,
is_receive: isReceive,
is_send: isSend,
mtokens: !!isReceive ? undefined : info.outgoing_amt_msat,
out_channel: !!isReceive ? undefined : outgoing.channel,
out_payment: !!isReceive ? undefined : outgoingId,
secret: bufferAsHex(settleEvent.preimage) || undefined,
timeout: !!isReceive ? undefined : outgoingCltvHeight,
tokens: !!isReceive ? undefined : tokens,
};
};