Liquidity Ads
The initiator of open_channel2, tx_init_rbf and splice_init can request funding from the remote node. The non-initiator node will:
- let the open-channel-interceptor plugin decide whether to lease liquidity for new channels or not, and how much
- always honor liquidity requests on existing channels (RBF and splice)
We currently don't modify commitment transactions to enforce the lease. This is different from https://github.com/lightning/bolts/pull/878 and instead matches https://github.com/lightning/bolts/pull/1145.
We currently use the temporary tlv tag 1337 while we're waiting for feedback on our spec proposal.
Liquidity ads are included in the node_announcement message, which lets buyers compare sellers and connect to sellers that provide rates they are comfortable with.
We store every liquidity purchase (whether we're buyer or seller) in the audit DB. This is important information when choosing which peers are worth keeping channels with.
Codecov Report
Attention: Patch coverage is 93.85475% with 22 lines in your changes are missing coverage. Please review.
Project coverage is 86.07%. Comparing base (
c8184b3) to head (74f2c0d).
:exclamation: Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@ Coverage Diff @@
## master #2550 +/- ##
==========================================
+ Coverage 85.96% 86.07% +0.11%
==========================================
Files 219 220 +1
Lines 18441 18709 +268
Branches 762 809 +47
==========================================
+ Hits 15853 16104 +251
- Misses 2588 2605 +17
| Files | Coverage Δ | |
|---|---|---|
| ...r-core/src/main/scala/fr/acinq/eclair/Eclair.scala | 56.61% <100.00%> (+0.18%) |
:arrow_up: |
| .../src/main/scala/fr/acinq/eclair/PluginParams.scala | 66.66% <ø> (ø) |
|
| ...in/scala/fr/acinq/eclair/channel/ChannelData.scala | 100.00% <ø> (ø) |
|
| .../scala/fr/acinq/eclair/channel/ChannelEvents.scala | 100.00% <100.00%> (ø) |
|
| ...c/main/scala/fr/acinq/eclair/channel/Helpers.scala | 94.56% <100.00%> (+0.02%) |
:arrow_up: |
| ...in/scala/fr/acinq/eclair/channel/fsm/Channel.scala | 85.47% <100.00%> (+0.27%) |
:arrow_up: |
| ...inq/eclair/channel/fund/InteractiveTxBuilder.scala | 91.80% <100.00%> (+0.42%) |
:arrow_up: |
| ...cinq/eclair/channel/fund/InteractiveTxFunder.scala | 92.80% <100.00%> (+0.32%) |
:arrow_up: |
| ...c/main/scala/fr/acinq/eclair/db/pg/PgAuditDb.scala | 99.73% <100.00%> (+0.04%) |
:arrow_up: |
| ...cala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala | 99.72% <100.00%> (+0.04%) |
:arrow_up: |
| ... and 16 more |
FYI, here is a patch on channel/Monitoring.scala to track liquidity splices:
diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Monitoring.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Monitoring.scala
index fa15594cc0..4900a46467 100644
--- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Monitoring.scala
+++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Monitoring.scala
@@ -57,10 +57,18 @@ object Monitoring {
*/
def recordSplice(fundingParams: InteractiveTxParams, sharedTx: SharedTransaction): Unit = {
if (fundingParams.localContribution > 0.sat) {
- Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Local).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceIn).record(fundingParams.localContribution.toLong)
+ if (fundingParams.isInitiator) {
+ Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Local).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceIn).record(fundingParams.localContribution.toLong)
+ } else {
+ Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Local).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceLiquidity).record(fundingParams.localContribution.toLong)
+ }
}
if (fundingParams.remoteContribution > 0.sat) {
- Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Remote).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceIn).record(fundingParams.remoteContribution.toLong)
+ if (fundingParams.isInitiator) {
+ Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Remote).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceLiquidity).record(fundingParams.remoteContribution.toLong)
+ } else {
+ Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Remote).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceIn).record(fundingParams.remoteContribution.toLong)
+ }
}
if (sharedTx.localOnlyNonChangeOutputs.nonEmpty) {
Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Local).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceOut).record(sharedTx.localOnlyNonChangeOutputs.map(_.amount).sum.toLong)
@@ -68,10 +76,10 @@ object Monitoring {
if (sharedTx.remoteOutputs.nonEmpty) {
Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Remote).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceOut).record(sharedTx.remoteOutputs.map(_.amount).sum.toLong)
}
- if (fundingParams.localContribution < 0.sat && sharedTx.localOutputs.isEmpty) {
+ if (fundingParams.localContribution < 0.sat && sharedTx.localOutputs.isEmpty && fundingParams.remoteContribution == 0.sat) {
Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Local).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceCpfp).record(Math.abs(fundingParams.localContribution.toLong))
}
- if (fundingParams.remoteContribution < 0.sat && sharedTx.remoteOutputs.isEmpty) {
+ if (fundingParams.remoteContribution < 0.sat && sharedTx.remoteOutputs.isEmpty && fundingParams.localContribution == 0.sat) {
Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Remote).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceCpfp).record(Math.abs(fundingParams.remoteContribution.toLong))
}
}
@@ -114,6 +122,7 @@ object Monitoring {
val SpliceIn = "splice-in"
val SpliceOut = "splice-out"
val SpliceCpfp = "splice-cpfp"
+ val SpliceLiquidity = "splice-liquidity"
}
}
A "purer" way would have been to add a isInitiator tag, instead of a SpliceLiquidity type, but it's also less user friendly. Here we are arbitrarily categorizing splices anyway. Will see how it goes on feature branches.
Please rebase 🙏
Rebased and squashed! I'll work on porting the annoying-to-rebase parts to master to simplify future rebases.
Superseded by #2848