Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Broadcasted transactions cleanup #242

Merged
merged 2 commits into from
May 23, 2024
Merged
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
2 changes: 1 addition & 1 deletion lib/android/src/main/java/com/reactnativeldk/LdkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1244,7 +1244,7 @@ class LdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMod
logDump.add("Open channel:")

channel._funding_txo?._txid?.let { txId ->
logDump.add("Funding txid: ${txId.hexEncodedString()}")
logDump.add("Funding txid: ${txId.reversedArray().hexEncodedString()}")
}

logDump.add("Ready: ${if (channel._is_channel_ready) "YES" else "NO"}")
Expand Down
2 changes: 1 addition & 1 deletion lib/ios/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ extension ChannelDetails {
"confirmations_required": getConfirmationsRequired() as Any, // Optional number
"short_channel_id": shortChannelId,
"inbound_scid_alias": getInboundScidAlias() != nil ? String(getInboundScidAlias()!) : shortChannelId, //String
"inbound_payment_scid": getInboundPaymentScid() as Any, //Optional number,
"inbound_payment_scid": getInboundPaymentScid() != nil ? String(getInboundPaymentScid()!) : "", //String,
"inbound_capacity_sat": getInboundCapacityMsat() / 1000,
"outbound_capacity_sat": getOutboundCapacityMsat() / 1000,
"channel_value_satoshis": getChannelValueSatoshis(),
Expand Down
99 changes: 99 additions & 0 deletions lib/src/lightning-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,13 @@
TCreatePaymentReq,
TBackupServerDetails,
IAddress,
TLspLogPayload,

Check warning on line 58 in lib/src/lightning-manager.ts

View workflow job for this annotation

GitHub Actions / Run lint check

'TLspLogPayload' is defined but never used
TLspLogEvent,
} from './utils/types';
import {
appendPath,
findOutputsFromRawTxs,
getTxIdFromRawTx,
isValidBech32mEncodedString,
parseData,
promiseTimeout,
Expand Down Expand Up @@ -362,7 +363,7 @@
this.account = account;
this.network = network;
this.addresses = await this.readAddressesFromFile();
this.getAddress = async () => {

Check warning on line 366 in lib/src/lightning-manager.ts

View workflow job for this annotation

GitHub Actions / Run lint check

Missing return type on function
const addressObj = await getAddress();
const address = addressObj?.address;
if (address) {
Expand Down Expand Up @@ -510,6 +511,8 @@
//Writes node state to log files
ldk.nodeStateDump().catch(console.error);

this.cleanupBroadcastedTxs().catch(console.error);

this.isStarting = false;
const result = ok('Node started');
this.resolveAllPendingStartPromises(result);
Expand Down Expand Up @@ -1586,6 +1589,102 @@
return [];
}

async cleanupBroadcastedTxs(): Promise<void> {
const savedTxs = await this.getLdkBroadcastedTxs();
if (savedTxs.length === 0) {
await ldk.writeToLogFile(
'debug',
'No broadcasted transactions found to cleanup.',
);
return;
}

const txsToCleanup: string[] = [];
for (const rawTx of savedTxs) {
const txid = getTxIdFromRawTx(rawTx);
const txData = await this.getTransactionData(txid);
if (!txData) {
await ldk.writeToLogFile(
'error',
`No txData found for tx: ${txid}. Skip cleanup.`,
);
//TODO maybe try broadcast and depending on the error, remove it if invalid inputs or something we expect from an invalid tx.
continue;
}

if (!txData.height) {
await ldk.writeToLogFile(
'error',
`Tx: ${txid} has no height (not in block). Skipping cleanup.`,
);
continue;
}

const numberOfConfirmations =
this.currentBlock.height - txData.height + 1;
if (numberOfConfirmations >= 6) {
await ldk.writeToLogFile(
'debug',
`Cleaning up tx: ${txid} with ${numberOfConfirmations} confirmations.`,
);
txsToCleanup.push(rawTx);
} else {
await ldk.writeToLogFile(
'debug',
`Tx: ${txid} with ${numberOfConfirmations} confirmations. Skipping cleanup.`,
);
}
}

if (txsToCleanup.length === 0) {
await ldk.writeToLogFile(
'debug',
'No broadcasted transactions found to cleanup.',
);
return;
}

//Fetch again in case of another process writing to the file since last lookup.
const latestSavedTxs = await this.getLdkBroadcastedTxs();
const txsToKeep = latestSavedTxs.filter((tx) => !txsToCleanup.includes(tx));

const saveRes = await ldk.writeToFile({
fileName: ELdkFiles.broadcasted_transactions,
content: JSON.stringify(txsToKeep),
remotePersist: false,
});
if (saveRes.isErr()) {
await ldk.writeToLogFile(
'error',
`Failed saving cleaned up txs: ${saveRes.error}`,
);
return;
}

//Save the cleanup up txs just in case. They won't ever be loaded automatically.
let currentConfirmedRawTxs: string[] = [];
const currentConfirmedRawTxsRes = await ldk.readFromFile({
fileName: ELdkFiles.confirmed_broadcasted_transactions,
});
if (currentConfirmedRawTxsRes.isOk()) {
currentConfirmedRawTxs = parseData(
currentConfirmedRawTxsRes.value.content,
[],
);
}

await ldk.writeToFile({
fileName: ELdkFiles.confirmed_broadcasted_transactions,
content: JSON.stringify(currentConfirmedRawTxs.concat(txsToCleanup)),
remotePersist: false,
});

await ldk.writeToLogFile(
'info',
`Cleaned up ${txsToCleanup.length} broadcasted transactions.`,
);
}

async rebroadcastAllKnownTransactions(): Promise<any[]> {
const broadcastedTransactions = await this.getLdkBroadcastedTxs();
const results = [];
Expand Down Expand Up @@ -2003,7 +2102,7 @@
}

private onChannelManagerPendingHtlcsForwardable(
res: TChannelManagerPendingHtlcsForwardable,

Check warning on line 2105 in lib/src/lightning-manager.ts

View workflow job for this annotation

GitHub Actions / Run lint check

'res' is defined but never used. Allowed unused args must match /^_/u
): void {
ldk.processPendingHtlcForwards().catch(console.error);
}
Expand Down
5 changes: 5 additions & 0 deletions lib/src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@
export const extractPaymentRequest = (paymentRequest: string): string => {
let result = paymentRequest.replace('lightning:', '').toLowerCase();
result = result.replace('bitcoin:', '').toLowerCase();
result = result.replace(/[^\x00-\x7F]/g, ''); //Strip anything not utf8 to avoid LDK crashes

Check warning on line 227 in lib/src/utils/helpers.ts

View workflow job for this annotation

GitHub Actions / Run lint check

Unexpected control character(s) in regular expression: \x00

//TODO support bip21 and other common formats
return result;
Expand Down Expand Up @@ -301,6 +301,11 @@
return result;
};

export const getTxIdFromRawTx = (rawTx: string): string => {
const tx = bitcoin.Transaction.fromHex(rawTx);
return tx.getId();
};

/**
* Pauses execution of a function.
* @param {number} ms The time to wait in milliseconds.
Expand Down
1 change: 1 addition & 0 deletions lib/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ export enum ELdkFiles {
peers = 'peers.json', //File saved from JS
unconfirmed_transactions = 'unconfirmed_transactions.json',
broadcasted_transactions = 'broadcasted_transactions.json',
confirmed_broadcasted_transactions = 'confirmed_broadcasted_transactions.json',
payment_ids = 'payment_ids.json',
spendable_outputs = 'spendable_outputs.json',
payments_claimed = 'payments_claimed.json', // Written in swift/kotlin and read from JS
Expand Down
Loading