handle batched channel opens. infer funding balances in both directions.

This commit is contained in:
Mononaut 2022-11-21 09:14:06 +09:00 committed by softsimon
parent 35ae672177
commit dc7d5bc94d
No known key found for this signature in database
GPG Key ID: 488D7DCFB5A430D7
2 changed files with 45 additions and 32 deletions

View File

@ -282,7 +282,7 @@ class ChannelsApi {
const query = ` const query = `
SELECT SELECT
channels.id, channels.node1_public_key, channels.node2_public_key, channels.id, channels.node1_public_key, channels.node2_public_key,
channels.closing_reason, channels.closing_transaction_id, channels.closing_reason, channels.closing_transaction_id, channels.capacity,
forensics.* forensics.*
FROM channels FROM channels
LEFT JOIN channels_forensics as forensics ON forensics.channel_id = channels.id LEFT JOIN channels_forensics as forensics ON forensics.channel_id = channels.id
@ -304,7 +304,7 @@ class ChannelsApi {
const query = ` const query = `
SELECT SELECT
channels.id, channels.node1_public_key, channels.node2_public_key, channels.id, channels.node1_public_key, channels.node2_public_key,
channels.status, channels.transaction_id, channels.status, channels.transaction_id, channels.capacity,
forensics.* forensics.*
FROM channels FROM channels
LEFT JOIN channels_forensics as forensics ON forensics.channel_id = channels.id LEFT JOIN channels_forensics as forensics ON forensics.channel_id = channels.id
@ -312,8 +312,10 @@ class ChannelsApi {
`; `;
const [rows]: any = await DB.query(query, [transactionId]); const [rows]: any = await DB.query(query, [transactionId]);
if (rows.length > 0) { if (rows.length > 0) {
rows[0].outputs = JSON.parse(rows[0].outputs); return rows.map(row => {
return rows[0]; row.outputs = JSON.parse(row.outputs);
return row;
});
} }
} catch (e) { } catch (e) {
logger.err('$getChannelForensicsByOpeningId error: ' + (e instanceof Error ? e.message : e)); logger.err('$getChannelForensicsByOpeningId error: ' + (e instanceof Error ? e.message : e));
@ -344,15 +346,15 @@ class ChannelsApi {
const jsonOutputs = JSON.stringify(channelInfo.outputs); const jsonOutputs = JSON.stringify(channelInfo.outputs);
await DB.query<ResultSetHeader>(query, [ await DB.query<ResultSetHeader>(query, [
channelInfo.id, channelInfo.id,
channelInfo.node1_closing_balance, channelInfo.node1_closing_balance || 0,
channelInfo.node2_closing_balance, channelInfo.node2_closing_balance || 0,
channelInfo.closed_by, channelInfo.closed_by,
channelInfo.closing_fee, channelInfo.closing_fee || 0,
jsonOutputs, jsonOutputs,
channelInfo.node1_closing_balance, channelInfo.node1_closing_balance || 0,
channelInfo.node2_closing_balance, channelInfo.node2_closing_balance || 0,
channelInfo.closed_by, channelInfo.closed_by,
channelInfo.closing_fee, channelInfo.closing_fee || 0,
jsonOutputs jsonOutputs
]); ]);
} catch (e) { } catch (e) {
@ -382,12 +384,12 @@ class ChannelsApi {
`; `;
await DB.query<ResultSetHeader>(query, [ await DB.query<ResultSetHeader>(query, [
channelInfo.id, channelInfo.id,
channelInfo.node1_funding_balance, channelInfo.node1_funding_balance || 0,
channelInfo.node2_funding_balance, channelInfo.node2_funding_balance || 0,
channelInfo.funding_ratio, channelInfo.funding_ratio,
channelInfo.single_funded ? 1 : 0, channelInfo.single_funded ? 1 : 0,
channelInfo.node1_funding_balance, channelInfo.node1_funding_balance || 0,
channelInfo.node2_funding_balance, channelInfo.node2_funding_balance || 0,
channelInfo.funding_ratio, channelInfo.funding_ratio,
channelInfo.single_funded ? 1 : 0, channelInfo.single_funded ? 1 : 0,
]); ]);

View File

@ -372,9 +372,12 @@ class NetworkSyncService {
// this input directly spends a channel close output // this input directly spends a channel close output
await this.$attributeChannelBalances(closeChannel, openChannel, input); await this.$attributeChannelBalances(closeChannel, openChannel, input);
} else { } else {
const prevOpenChannel = await channelsApi.$getChannelForensicsByOpeningId(input.txid); const prevOpenChannels = await channelsApi.$getChannelForensicsByOpeningId(input.txid);
if (prevOpenChannel) { if (prevOpenChannels?.length) {
// this input spends a channel open change output
for (const prevOpenChannel of prevOpenChannels) {
await this.$attributeChannelBalances(prevOpenChannel, openChannel, input, null, null, true); await this.$attributeChannelBalances(prevOpenChannel, openChannel, input, null, null, true);
}
} else { } else {
// check if this input spends any swept channel close outputs // check if this input spends any swept channel close outputs
await this.$attributeSweptChannelCloses(openChannel, input); await this.$attributeSweptChannelCloses(openChannel, input);
@ -435,33 +438,33 @@ class NetworkSyncService {
): Promise<void> { ): Promise<void> {
// figure out which node controls the input/output // figure out which node controls the input/output
let openSide; let openSide;
let closeLocal; let prevLocal;
let closeRemote; let prevRemote;
let matched = false; let matched = false;
let ambiguous = false; // if counterparties are the same in both channels, we can't tell them apart let ambiguous = false; // if counterparties are the same in both channels, we can't tell them apart
if (openChannel.node1_public_key === prevChannel.node1_public_key) { if (openChannel.node1_public_key === prevChannel.node1_public_key) {
openSide = 1; openSide = 1;
closeLocal = 1; prevLocal = 1;
closeRemote = 2; prevRemote = 2;
matched = true; matched = true;
} else if (openChannel.node1_public_key === prevChannel.node2_public_key) { } else if (openChannel.node1_public_key === prevChannel.node2_public_key) {
openSide = 1; openSide = 1;
closeLocal = 2; prevLocal = 2;
closeRemote = 1; prevRemote = 1;
matched = true; matched = true;
} }
if (openChannel.node2_public_key === prevChannel.node1_public_key) { if (openChannel.node2_public_key === prevChannel.node1_public_key) {
openSide = 2; openSide = 2;
closeLocal = 1; prevLocal = 1;
closeRemote = 2; prevRemote = 2;
if (matched) { if (matched) {
ambiguous = true; ambiguous = true;
} }
matched = true; matched = true;
} else if (openChannel.node2_public_key === prevChannel.node2_public_key) { } else if (openChannel.node2_public_key === prevChannel.node2_public_key) {
openSide = 2; openSide = 2;
closeLocal = 2; prevLocal = 2;
closeRemote = 1; prevRemote = 1;
if (matched) { if (matched) {
ambiguous = true; ambiguous = true;
} }
@ -508,7 +511,7 @@ class NetworkSyncService {
} }
// attribute outputs to each counterparty, and sum up total known balances // attribute outputs to each counterparty, and sum up total known balances
prevChannel.outputs[input.vout].node = closeLocal; prevChannel.outputs[input.vout].node = prevLocal;
const isPenalty = prevChannel.outputs.filter((out) => out.type === 2 || out.type === 4)?.length > 0; const isPenalty = prevChannel.outputs.filter((out) => out.type === 2 || out.type === 4)?.length > 0;
const normalOutput = [1,3].includes(prevChannel.outputs[input.vout].type); const normalOutput = [1,3].includes(prevChannel.outputs[input.vout].type);
let localClosingBalance = 0; let localClosingBalance = 0;
@ -519,7 +522,7 @@ class NetworkSyncService {
localClosingBalance += output.value; localClosingBalance += output.value;
} else if (output.node) { } else if (output.node) {
// this output determinstically linked to one of the counterparties // this output determinstically linked to one of the counterparties
if (output.node === closeLocal) { if (output.node === prevLocal) {
localClosingBalance += output.value; localClosingBalance += output.value;
} else { } else {
remoteClosingBalance += output.value; remoteClosingBalance += output.value;
@ -529,17 +532,25 @@ class NetworkSyncService {
remoteClosingBalance += output.value; remoteClosingBalance += output.value;
} }
} }
prevChannel[`node${closeLocal}_closing_balance`] = localClosingBalance; prevChannel[`node${prevLocal}_closing_balance`] = localClosingBalance;
prevChannel[`node${closeRemote}_closing_balance`] = remoteClosingBalance; prevChannel[`node${prevRemote}_closing_balance`] = remoteClosingBalance;
prevChannel.closing_fee = prevChannelTx.fee; prevChannel.closing_fee = prevChannelTx.fee;
if (initiator && !linkedOpenings) { if (initiator && !linkedOpenings) {
const initiatorSide = initiator === 'remote' ? closeRemote : closeLocal; const initiatorSide = initiator === 'remote' ? prevRemote : prevLocal;
prevChannel.closed_by = prevChannel[`node${initiatorSide}_public_key`]; prevChannel.closed_by = prevChannel[`node${initiatorSide}_public_key`];
} }
// save changes to the closing channel // save changes to the closing channel
await channelsApi.$updateClosingInfo(prevChannel); await channelsApi.$updateClosingInfo(prevChannel);
} else {
if (prevChannelTx.vin.length <= 1) {
prevChannel[`node${prevLocal}_funding_balance`] = prevChannel.capacity;
prevChannel.single_funded = true;
prevChannel.funding_ratio = 1;
// save changes to the closing channel
await channelsApi.$updateOpeningInfo(prevChannel);
}
} }
openChannel[`node${openSide}_funding_balance`] = openChannel[`node${openSide}_funding_balance`] + (openContribution || prevChannelTx?.vout[input.vout]?.value || 0); openChannel[`node${openSide}_funding_balance`] = openChannel[`node${openSide}_funding_balance`] + (openContribution || prevChannelTx?.vout[input.vout]?.value || 0);
} }