From 3d0dc6832fdb86a015edc0335a49a02bd49ee69b Mon Sep 17 00:00:00 2001 From: rorp Date: Fri, 3 Mar 2023 09:29:40 -0800 Subject: [PATCH 1/5] listpaidinvoices and listexpiredinvoices RPC calls --- contrib/eclair-cli.bash-completion | 2 +- .../main/scala/fr/acinq/eclair/Eclair.scala | 12 ++++++++++++ .../fr/acinq/eclair/db/DualDatabases.scala | 12 ++++++------ .../scala/fr/acinq/eclair/db/PaymentsDb.scala | 4 ++-- .../fr/acinq/eclair/db/pg/PgPaymentsDb.scala | 8 ++++---- .../eclair/db/sqlite/SqlitePaymentsDb.scala | 8 ++++---- .../eclair/payment/receive/InvoicePurger.scala | 2 +- .../fr/acinq/eclair/db/PaymentsDbSpec.scala | 14 +++++++------- .../payment/receive/InvoicePurgerSpec.scala | 10 +++++----- .../fr/acinq/eclair/api/handlers/Invoice.scala | 18 +++++++++++++++++- 10 files changed, 59 insertions(+), 31 deletions(-) diff --git a/contrib/eclair-cli.bash-completion b/contrib/eclair-cli.bash-completion index b6bb784e5d..6f1b660789 100644 --- a/contrib/eclair-cli.bash-completion +++ b/contrib/eclair-cli.bash-completion @@ -21,7 +21,7 @@ _eclair-cli() *) # works fine, but is too slow at the moment. # allopts=$($eclaircli help 2>&1 | awk '$1 ~ /^"/ { sub(/,/, ""); print $1}' | sed 's/[":]//g') - allopts="getinfo connect open close forceclose updaterelayfee peers channels channel allnodes allchannels allupdates findroute findroutetonode findroutebetweennodes parseinvoice payinvoice sendtonode getsentinfo createinvoice getinvoice listinvoices listpendinginvoices getreceivedinfo audit networkfees channelstats" + allopts="getinfo connect open close forceclose updaterelayfee peers channels channel allnodes allchannels allupdates findroute findroutetonode findroutebetweennodes parseinvoice payinvoice sendtonode getsentinfo createinvoice getinvoice listinvoices listpendinginvoices listpaidinvoices listexpiredinvoices getreceivedinfo audit networkfees channelstats" if ! [[ " $allopts " =~ " $prev " ]]; then # prevent double arguments if [[ -z "$cur" || "$cur" =~ ^[a-z] ]]; then diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala index 5ed7c86140..efb8a5708f 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala @@ -139,6 +139,10 @@ trait Eclair { def pendingInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] + def paidInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] + + def expiredInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] + def allInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] def deleteInvoice(paymentHash: ByteVector32): Future[String] @@ -461,6 +465,14 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging { appKit.nodeParams.db.payments.listPendingIncomingPayments(from.toTimestampMilli, to.toTimestampMilli, paginated_opt).map(_.invoice) } + override def paidInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] = Future { + appKit.nodeParams.db.payments.listReceivedIncomingPayments(from.toTimestampMilli, to.toTimestampMilli, paginated_opt).map(_.invoice) + } + + override def expiredInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] = Future { + appKit.nodeParams.db.payments.listExpiredIncomingPayments(from.toTimestampMilli, to.toTimestampMilli, paginated_opt).map(_.invoice) + } + override def getInvoice(paymentHash: ByteVector32)(implicit timeout: Timeout): Future[Option[Invoice]] = Future { appKit.nodeParams.db.payments.getIncomingPayment(paymentHash).map(_.invoice) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/DualDatabases.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/DualDatabases.scala index 5478078931..700b503c32 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/DualDatabases.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/DualDatabases.scala @@ -316,14 +316,14 @@ case class DualPaymentsDb(primary: PaymentsDb, secondary: PaymentsDb) extends Pa primary.listPendingIncomingPayments(from, to, paginated_opt) } - override def listExpiredIncomingPayments(from: TimestampMilli, to: TimestampMilli): Seq[IncomingPayment] = { - runAsync(secondary.listExpiredIncomingPayments(from, to)) - primary.listExpiredIncomingPayments(from, to) + override def listExpiredIncomingPayments(from: TimestampMilli, to: TimestampMilli, paginated_opt: Option[Paginated]): Seq[IncomingPayment] = { + runAsync(secondary.listExpiredIncomingPayments(from, to, paginated_opt)) + primary.listExpiredIncomingPayments(from, to, paginated_opt) } - override def listReceivedIncomingPayments(from: TimestampMilli, to: TimestampMilli): Seq[IncomingPayment] = { - runAsync(secondary.listReceivedIncomingPayments(from, to)) - primary.listReceivedIncomingPayments(from, to) + override def listReceivedIncomingPayments(from: TimestampMilli, to: TimestampMilli, paginated_opt: Option[Paginated]): Seq[IncomingPayment] = { + runAsync(secondary.listReceivedIncomingPayments(from, to, paginated_opt)) + primary.listReceivedIncomingPayments(from, to, paginated_opt) } override def addOutgoingPayment(outgoingPayment: OutgoingPayment): Unit = { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala index bae8e22dd1..5a15405b03 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala @@ -58,10 +58,10 @@ trait IncomingPaymentsDb { def listPendingIncomingPayments(from: TimestampMilli, to: TimestampMilli, paginated_opt: Option[Paginated]): Seq[IncomingPayment] /** List all expired (not paid) incoming payments in the given time range (milli-seconds). */ - def listExpiredIncomingPayments(from: TimestampMilli, to: TimestampMilli): Seq[IncomingPayment] + def listExpiredIncomingPayments(from: TimestampMilli, to: TimestampMilli, paginated_opt: Option[Paginated]): Seq[IncomingPayment] /** List all received (paid) incoming payments in the given time range (milli-seconds). */ - def listReceivedIncomingPayments(from: TimestampMilli, to: TimestampMilli): Seq[IncomingPayment] + def listReceivedIncomingPayments(from: TimestampMilli, to: TimestampMilli, paginated_opt: Option[Paginated]): Seq[IncomingPayment] } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/pg/PgPaymentsDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/pg/PgPaymentsDb.scala index 72cdba09ba..b11ee902b0 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/pg/PgPaymentsDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/pg/PgPaymentsDb.scala @@ -363,9 +363,9 @@ class PgPaymentsDb(implicit ds: DataSource, lock: PgLock) extends PaymentsDb wit } } - override def listReceivedIncomingPayments(from: TimestampMilli, to: TimestampMilli): Seq[IncomingPayment] = withMetrics("payments/list-incoming-received", DbBackends.Postgres) { + override def listReceivedIncomingPayments(from: TimestampMilli, to: TimestampMilli, paginated_opt: Option[Paginated]): Seq[IncomingPayment] = withMetrics("payments/list-incoming-received", DbBackends.Postgres) { withLock { pg => - using(pg.prepareStatement("SELECT * FROM payments.received WHERE received_msat > 0 AND created_at > ? AND created_at < ? ORDER BY created_at")) { statement => + using(pg.prepareStatement(limited("SELECT * FROM payments.received WHERE received_msat > 0 AND created_at > ? AND created_at < ? ORDER BY created_at", paginated_opt))) { statement => statement.setTimestamp(1, from.toSqlTimestamp) statement.setTimestamp(2, to.toSqlTimestamp) statement.executeQuery().flatMap(parseIncomingPayment).toSeq @@ -384,9 +384,9 @@ class PgPaymentsDb(implicit ds: DataSource, lock: PgLock) extends PaymentsDb wit } } - override def listExpiredIncomingPayments(from: TimestampMilli, to: TimestampMilli): Seq[IncomingPayment] = withMetrics("payments/list-incoming-expired", DbBackends.Postgres) { + override def listExpiredIncomingPayments(from: TimestampMilli, to: TimestampMilli, paginated_opt: Option[Paginated]): Seq[IncomingPayment] = withMetrics("payments/list-incoming-expired", DbBackends.Postgres) { withLock { pg => - using(pg.prepareStatement("SELECT * FROM payments.received WHERE received_msat IS NULL AND created_at > ? AND created_at < ? AND expire_at < ? ORDER BY created_at")) { statement => + using(pg.prepareStatement(limited("SELECT * FROM payments.received WHERE received_msat IS NULL AND created_at > ? AND created_at < ? AND expire_at < ? ORDER BY created_at", paginated_opt))) { statement => statement.setTimestamp(1, from.toSqlTimestamp) statement.setTimestamp(2, to.toSqlTimestamp) statement.setTimestamp(3, Timestamp.from(Instant.now())) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala index cd04eb98d1..409c551e0f 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala @@ -360,8 +360,8 @@ class SqlitePaymentsDb(val sqlite: Connection) extends PaymentsDb with Logging { } } - override def listReceivedIncomingPayments(from: TimestampMilli, to: TimestampMilli): Seq[IncomingPayment] = withMetrics("payments/list-incoming-received", DbBackends.Sqlite) { - using(sqlite.prepareStatement("SELECT * FROM received_payments WHERE received_msat > 0 AND created_at > ? AND created_at < ? ORDER BY created_at")) { statement => + override def listReceivedIncomingPayments(from: TimestampMilli, to: TimestampMilli, paginated_opt: Option[Paginated]): Seq[IncomingPayment] = withMetrics("payments/list-incoming-received", DbBackends.Sqlite) { + using(sqlite.prepareStatement(limited("SELECT * FROM received_payments WHERE received_msat > 0 AND created_at > ? AND created_at < ? ORDER BY created_at", paginated_opt))) { statement => statement.setLong(1, from.toLong) statement.setLong(2, to.toLong) statement.executeQuery().flatMap(parseIncomingPayment).toSeq @@ -377,8 +377,8 @@ class SqlitePaymentsDb(val sqlite: Connection) extends PaymentsDb with Logging { } } - override def listExpiredIncomingPayments(from: TimestampMilli, to: TimestampMilli): Seq[IncomingPayment] = withMetrics("payments/list-incoming-expired", DbBackends.Sqlite) { - using(sqlite.prepareStatement("SELECT * FROM received_payments WHERE received_msat IS NULL AND created_at > ? AND created_at < ? AND expire_at < ? ORDER BY created_at")) { statement => + override def listExpiredIncomingPayments(from: TimestampMilli, to: TimestampMilli, paginated_opt: Option[Paginated]): Seq[IncomingPayment] = withMetrics("payments/list-incoming-expired", DbBackends.Sqlite) { + using(sqlite.prepareStatement(limited("SELECT * FROM received_payments WHERE received_msat IS NULL AND created_at > ? AND created_at < ? AND expire_at < ? ORDER BY created_at", paginated_opt))) { statement => statement.setLong(1, from.toLong) statement.setLong(2, to.toLong) statement.setLong(3, TimestampMilli.now().toLong) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/receive/InvoicePurger.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/receive/InvoicePurger.scala index 28d96061cf..978a9680f9 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/receive/InvoicePurger.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/receive/InvoicePurger.scala @@ -58,7 +58,7 @@ class InvoicePurger private(paymentsDb: IncomingPaymentsDb, context: ActorContex case TickPurge => val now = TimestampMilli.now() val start = if (fullScan) 0 unixms else now - 15.days - val expiredPayments = paymentsDb.listExpiredIncomingPayments(start, now) + val expiredPayments = paymentsDb.listExpiredIncomingPayments(start, now, None) // purge expired payments expiredPayments.foreach(p => paymentsDb.removeIncomingPayment(p.invoice.paymentHash)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala index 3ab501ab6d..71f498dfa2 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/db/PaymentsDbSpec.scala @@ -207,7 +207,7 @@ class PaymentsDbSpec extends AnyFunSuite { assert(db.listOutgoingPayments(1 unixms, 2000 unixms) == Seq(ps1, ps2, ps3, ps4, ps5, ps6)) assert(db.listIncomingPayments(1 unixms, TimestampMilli.now(), None) == Seq(pr1, pr2, pr3)) - assert(db.listExpiredIncomingPayments(1 unixms, 2000 unixms) == Seq(pr2)) + assert(db.listExpiredIncomingPayments(1 unixms, 2000 unixms, None) == Seq(pr2)) }) } @@ -511,9 +511,9 @@ class PaymentsDbSpec extends AnyFunSuite { assert(db.listIncomingPayments(TimestampMilli(Instant.parse("2020-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2100-12-31T23:59:59.00Z").toEpochMilli), None) == Seq(pr2, pr1)) assert(db.listIncomingPayments(TimestampMilli(Instant.parse("2020-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2020-12-31T23:59:59.00Z").toEpochMilli), None) == Seq(pr2)) assert(db.listIncomingPayments(TimestampMilli(Instant.parse("2010-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2011-12-31T23:59:59.00Z").toEpochMilli), None) == Seq.empty) - assert(db.listExpiredIncomingPayments(TimestampMilli(Instant.parse("2020-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2100-12-31T23:59:59.00Z").toEpochMilli)) == Seq(pr2)) - assert(db.listExpiredIncomingPayments(TimestampMilli(Instant.parse("2020-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2020-12-31T23:59:59.00Z").toEpochMilli)) == Seq(pr2)) - assert(db.listExpiredIncomingPayments(TimestampMilli(Instant.parse("2010-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2011-12-31T23:59:59.00Z").toEpochMilli)) == Seq.empty) + assert(db.listExpiredIncomingPayments(TimestampMilli(Instant.parse("2020-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2100-12-31T23:59:59.00Z").toEpochMilli), None) == Seq(pr2)) + assert(db.listExpiredIncomingPayments(TimestampMilli(Instant.parse("2020-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2020-12-31T23:59:59.00Z").toEpochMilli), None) == Seq(pr2)) + assert(db.listExpiredIncomingPayments(TimestampMilli(Instant.parse("2010-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2011-12-31T23:59:59.00Z").toEpochMilli), None) == Seq.empty) assert(db.listOutgoingPayments(TimestampMilli(Instant.parse("2020-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2021-12-31T23:59:59.00Z").toEpochMilli)) == Seq(ps2, ps1, ps3)) assert(db.listOutgoingPayments(TimestampMilli(Instant.parse("2010-01-01T00:00:00.00Z").toEpochMilli), TimestampMilli(Instant.parse("2021-01-15T23:59:59.00Z").toEpochMilli)) == Seq(ps2, ps1)) @@ -701,8 +701,8 @@ class PaymentsDbSpec extends AnyFunSuite { val now = TimestampMilli.now() assert(db.listIncomingPayments(0 unixms, now, None) == Seq(expiredPayment1, expiredPayment2, expiredPayment3, pendingPayment1, pendingPayment2, pendingPayment3, payment1.copy(status = IncomingPaymentStatus.Pending), payment2.copy(status = IncomingPaymentStatus.Pending), payment3.copy(status = IncomingPaymentStatus.Pending))) - assert(db.listExpiredIncomingPayments(0 unixms, now) == Seq(expiredPayment1, expiredPayment2, expiredPayment3)) - assert(db.listReceivedIncomingPayments(0 unixms, now) == Nil) + assert(db.listExpiredIncomingPayments(0 unixms, now, None) == Seq(expiredPayment1, expiredPayment2, expiredPayment3)) + assert(db.listReceivedIncomingPayments(0 unixms, now, None) == Nil) assert(db.listPendingIncomingPayments(0 unixms, now, None) == Seq(pendingPayment1, pendingPayment2, pendingPayment3, payment1.copy(status = IncomingPaymentStatus.Pending), payment2.copy(status = IncomingPaymentStatus.Pending), payment3.copy(status = IncomingPaymentStatus.Pending))) db.receiveIncomingPayment(paidInvoice1.paymentHash, 461 msat, receivedAt1) @@ -722,7 +722,7 @@ class PaymentsDbSpec extends AnyFunSuite { assert(db.listIncomingPayments(0 unixms, now, Some(Paginated(3, 3))) == Seq(pendingPayment1, pendingPayment2, pendingPayment3)) assert(db.listPendingIncomingPayments(0 unixms, now, None) == Seq(pendingPayment1, pendingPayment2, pendingPayment3)) assert(db.listPendingIncomingPayments(0 unixms, now, Some(Paginated(1, 1))) == Seq(pendingPayment2)) - assert(db.listReceivedIncomingPayments(0 unixms, now) == Seq(payment1, payment2, payment3)) + assert(db.listReceivedIncomingPayments(0 unixms, now, None) == Seq(payment1, payment2, payment3)) assert(db.removeIncomingPayment(paidInvoice1.paymentHash).isFailure) db.removeIncomingPayment(paidInvoice1.paymentHash).failed.foreach(e => assert(e.getMessage == "Cannot remove a received incoming payment")) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/receive/InvoicePurgerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/receive/InvoicePurgerSpec.scala index 0093fb5bde..e019b0bb01 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/receive/InvoicePurgerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/receive/InvoicePurgerSpec.scala @@ -63,8 +63,8 @@ class InvoicePurgerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("ap assert(db.listIncomingPayments(0 unixms, now, None) == expiredPayments ++ pendingPayments ++ paidPayments) assert(db.listIncomingPayments(now - 100.days, now, None) == pendingPayments ++ paidPayments) assert(db.listPendingIncomingPayments(0 unixms, now, None) == pendingPayments) - assert(db.listReceivedIncomingPayments(0 unixms, now) == paidPayments) - assert(db.listExpiredIncomingPayments(0 unixms, now) == expiredPayments) + assert(db.listReceivedIncomingPayments(0 unixms, now, None) == paidPayments) + assert(db.listExpiredIncomingPayments(0 unixms, now, None) == expiredPayments) val probe = testKit.createTestProbe[PurgeEvent]() system.eventStream ! EventStream.Subscribe(probe.ref) @@ -74,7 +74,7 @@ class InvoicePurgerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("ap // check that purge runs before the default first interval of 24 hours probe.expectMessage(5 seconds, PurgeCompleted) probe.expectNoMessage() - assert(db.listExpiredIncomingPayments(0 unixms, now).isEmpty) + assert(db.listExpiredIncomingPayments(0 unixms, now, None).isEmpty) assert(db.listIncomingPayments(0 unixms, now, None) == pendingPayments ++ paidPayments) testKit.stop(purger) @@ -105,7 +105,7 @@ class InvoicePurgerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("ap // check that the initial purge scanned the entire database probe.expectMessage(10 seconds, PurgeCompleted) probe.expectNoMessage() - assert(db.listExpiredIncomingPayments(0 unixms, TimestampMilli.now()).isEmpty) + assert(db.listExpiredIncomingPayments(0 unixms, TimestampMilli.now(), None).isEmpty) // add an expired invoice from before the 15 days look back period val expiredInvoice3 = Bolt11Invoice(Block.TestnetGenesisBlock.hash, Some(100 msat), randomBytes32(), alicePriv, Left("expired invoice3"), CltvExpiryDelta(18), @@ -122,7 +122,7 @@ class InvoicePurgerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("ap // check that subsequent purge runs do not go back > 15 days probe.expectMessage(10 seconds, PurgeCompleted) probe.expectNoMessage() - assert(db.listExpiredIncomingPayments(0 unixms, TimestampMilli.now()) == Seq(expiredPayment3)) + assert(db.listExpiredIncomingPayments(0 unixms, TimestampMilli.now(), None) == Seq(expiredPayment3)) testKit.stop(purger) } diff --git a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala index 025efc8f6e..bebcb378df 100644 --- a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala +++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala @@ -57,6 +57,22 @@ trait Invoice { } } + val listPaidInvoices: Route = postRequest("listpaidinvoices") { implicit t => + withPaginated { paginated_opt => + formFields(fromFormParam(), toFormParam()) { (from, to) => + complete(eclairApi.paidInvoices(from, to, paginated_opt)) + } + } + } + + val listExpiredInvoices: Route = postRequest("listexpiredinvoices") { implicit t => + withPaginated { paginated_opt => + formFields(fromFormParam(), toFormParam()) { (from, to) => + complete(eclairApi.expiredInvoices(from, to, paginated_opt)) + } + } + } + val parseInvoice: Route = postRequest("parseinvoice") { implicit t => formFields(invoiceFormParam) { invoice => complete(invoice) @@ -69,6 +85,6 @@ trait Invoice { } } - val invoiceRoutes: Route = createInvoice ~ getInvoice ~ listInvoices ~ listPendingInvoices ~ parseInvoice ~ deleteInvoice + val invoiceRoutes: Route = createInvoice ~ getInvoice ~ listInvoices ~ listPendingInvoices ~ listPaidInvoices ~ listExpiredInvoices ~ parseInvoice ~ deleteInvoice } From b57bfefc0e3211acb275ceb2f7f711d779d39669 Mon Sep 17 00:00:00 2001 From: rorp Date: Fri, 3 Mar 2023 09:35:53 -0800 Subject: [PATCH 2/5] update release notes --- docs/release-notes/eclair-vnext.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/eclair-vnext.md b/docs/release-notes/eclair-vnext.md index 4a28740dad..a4f93ddd06 100644 --- a/docs/release-notes/eclair-vnext.md +++ b/docs/release-notes/eclair-vnext.md @@ -26,6 +26,7 @@ Offers are still experimental and some details could still change before they ar - `channel-created` is a new websocket event that is published when a channel's funding transaction has been broadcast (#2567) - `channel-opened` websocket event was updated to contain the final `channel_id` and be published when a channel is ready to process payments (#2567) - `getsentinfo` can now be used with `--offer` to list payments sent to a specific offer. +- `listpaidinvoices` and `listexpiredinvoices` allow to query paid and expired invoices respectively (#2607) ### Miscellaneous improvements and bug fixes From eb40988c4c7fb15988bba7840498136f7fd75b43 Mon Sep 17 00:00:00 2001 From: rorp Date: Sun, 26 Mar 2023 11:15:00 -0700 Subject: [PATCH 3/5] rename listpaidinvoices to listreceivedpayments --- docs/release-notes/eclair-vnext.md | 3 ++- .../src/main/scala/fr/acinq/eclair/Eclair.scala | 8 ++++---- .../scala/fr/acinq/eclair/api/handlers/Invoice.scala | 10 +--------- .../scala/fr/acinq/eclair/api/handlers/Payment.scala | 10 +++++++++- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/release-notes/eclair-vnext.md b/docs/release-notes/eclair-vnext.md index 023d486d4a..cb3d9d9afa 100644 --- a/docs/release-notes/eclair-vnext.md +++ b/docs/release-notes/eclair-vnext.md @@ -26,7 +26,8 @@ Offers are still experimental and some details could still change before they ar - `channel-created` is a new websocket event that is published when a channel's funding transaction has been broadcast (#2567) - `channel-opened` websocket event was updated to contain the final `channel_id` and be published when a channel is ready to process payments (#2567) - `getsentinfo` can now be used with `--offer` to list payments sent to a specific offer. -- `listpaidinvoices` and `listexpiredinvoices` allow to query paid and expired invoices respectively (#2607) +- `listreceivedpayments` allos to query received paid (#2607) +- `listexpiredinvoices` returns expired invoices (#2607) ### Miscellaneous improvements and bug fixes diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala index b2bd82c23f..15f54415f5 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala @@ -113,6 +113,8 @@ trait Eclair { def receivedInfo(paymentHash: ByteVector32)(implicit timeout: Timeout): Future[Option[IncomingPayment]] + def receivedPayments(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[IncomingPayment]] + def send(externalId_opt: Option[String], amount: MilliSatoshi, invoice: Bolt11Invoice, maxAttempts_opt: Option[Int] = None, maxFeeFlat_opt: Option[Satoshi] = None, maxFeePct_opt: Option[Double] = None, pathFindingExperimentName_opt: Option[String] = None)(implicit timeout: Timeout): Future[UUID] def sendBlocking(externalId_opt: Option[String], amount: MilliSatoshi, invoice: Bolt11Invoice, maxAttempts_opt: Option[Int] = None, maxFeeFlat_opt: Option[Satoshi] = None, maxFeePct_opt: Option[Double] = None, pathFindingExperimentName_opt: Option[String] = None)(implicit timeout: Timeout): Future[PaymentEvent] @@ -139,8 +141,6 @@ trait Eclair { def pendingInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] - def paidInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] - def expiredInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] def allInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] @@ -465,8 +465,8 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging { appKit.nodeParams.db.payments.listPendingIncomingPayments(from.toTimestampMilli, to.toTimestampMilli, paginated_opt).map(_.invoice) } - override def paidInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] = Future { - appKit.nodeParams.db.payments.listReceivedIncomingPayments(from.toTimestampMilli, to.toTimestampMilli, paginated_opt).map(_.invoice) + override def receivedPayments(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[IncomingPayment]] = Future { + appKit.nodeParams.db.payments.listReceivedIncomingPayments(from.toTimestampMilli, to.toTimestampMilli, paginated_opt) } override def expiredInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] = Future { diff --git a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala index bebcb378df..65523427e1 100644 --- a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala +++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala @@ -57,14 +57,6 @@ trait Invoice { } } - val listPaidInvoices: Route = postRequest("listpaidinvoices") { implicit t => - withPaginated { paginated_opt => - formFields(fromFormParam(), toFormParam()) { (from, to) => - complete(eclairApi.paidInvoices(from, to, paginated_opt)) - } - } - } - val listExpiredInvoices: Route = postRequest("listexpiredinvoices") { implicit t => withPaginated { paginated_opt => formFields(fromFormParam(), toFormParam()) { (from, to) => @@ -85,6 +77,6 @@ trait Invoice { } } - val invoiceRoutes: Route = createInvoice ~ getInvoice ~ listInvoices ~ listPendingInvoices ~ listPaidInvoices ~ listExpiredInvoices ~ parseInvoice ~ deleteInvoice + val invoiceRoutes: Route = createInvoice ~ getInvoice ~ listInvoices ~ listPendingInvoices ~ listExpiredInvoices ~ parseInvoice ~ deleteInvoice } diff --git a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Payment.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Payment.scala index ea920a53ce..c55a368b91 100644 --- a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Payment.scala +++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Payment.scala @@ -95,6 +95,14 @@ trait Payment { } } + val listReceivedPayments: Route = postRequest("listreceivedpayments") { implicit t => + withPaginated { paginated_opt => + formFields(fromFormParam(), toFormParam()) { (from, to) => + complete(eclairApi.receivedPayments(from, to, paginated_opt)) + } + } + } + val payOffer: Route = postRequest("payoffer") { implicit t => formFields(offerFormParam, amountMsatFormParam, "quantity".as[Long].?, "maxAttempts".as[Int].?, "maxFeeFlatSat".as[Satoshi].?, "maxFeePct".as[Double].?, "externalId".?, "pathFindingExperimentName".?, "blocking".as[Boolean].?) { case (offer, amountMsat, quantity_opt, maxAttempts_opt, maxFeeFlat_opt, maxFeePct_opt, externalId_opt, pathFindingExperimentName_opt, blocking_opt) => @@ -105,6 +113,6 @@ trait Payment { } } - val paymentRoutes: Route = usableBalances ~ payInvoice ~ sendToNode ~ sendToRoute ~ getSentInfo ~ getReceivedInfo ~ payOffer + val paymentRoutes: Route = usableBalances ~ payInvoice ~ sendToNode ~ sendToRoute ~ getSentInfo ~ getReceivedInfo ~ listReceivedPayments ~ payOffer } From 758d6229bd843df14d3d98183342470928a7dbff Mon Sep 17 00:00:00 2001 From: rorp Date: Sun, 2 Apr 2023 08:40:12 -0700 Subject: [PATCH 4/5] remove listexpiredinvoices --- docs/release-notes/eclair-vnext.md | 1 - .../src/main/scala/fr/acinq/eclair/Eclair.scala | 6 ------ .../scala/fr/acinq/eclair/api/handlers/Invoice.scala | 10 +--------- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/docs/release-notes/eclair-vnext.md b/docs/release-notes/eclair-vnext.md index cb3d9d9afa..6db9601528 100644 --- a/docs/release-notes/eclair-vnext.md +++ b/docs/release-notes/eclair-vnext.md @@ -27,7 +27,6 @@ Offers are still experimental and some details could still change before they ar - `channel-opened` websocket event was updated to contain the final `channel_id` and be published when a channel is ready to process payments (#2567) - `getsentinfo` can now be used with `--offer` to list payments sent to a specific offer. - `listreceivedpayments` allos to query received paid (#2607) -- `listexpiredinvoices` returns expired invoices (#2607) ### Miscellaneous improvements and bug fixes diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala index 15f54415f5..9d95b856aa 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala @@ -141,8 +141,6 @@ trait Eclair { def pendingInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] - def expiredInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] - def allInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] def deleteInvoice(paymentHash: ByteVector32): Future[String] @@ -469,10 +467,6 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging { appKit.nodeParams.db.payments.listReceivedIncomingPayments(from.toTimestampMilli, to.toTimestampMilli, paginated_opt) } - override def expiredInvoices(from: TimestampSecond, to: TimestampSecond, paginated_opt: Option[Paginated])(implicit timeout: Timeout): Future[Seq[Invoice]] = Future { - appKit.nodeParams.db.payments.listExpiredIncomingPayments(from.toTimestampMilli, to.toTimestampMilli, paginated_opt).map(_.invoice) - } - override def getInvoice(paymentHash: ByteVector32)(implicit timeout: Timeout): Future[Option[Invoice]] = Future { appKit.nodeParams.db.payments.getIncomingPayment(paymentHash).map(_.invoice) } diff --git a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala index 65523427e1..025efc8f6e 100644 --- a/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala +++ b/eclair-node/src/main/scala/fr/acinq/eclair/api/handlers/Invoice.scala @@ -57,14 +57,6 @@ trait Invoice { } } - val listExpiredInvoices: Route = postRequest("listexpiredinvoices") { implicit t => - withPaginated { paginated_opt => - formFields(fromFormParam(), toFormParam()) { (from, to) => - complete(eclairApi.expiredInvoices(from, to, paginated_opt)) - } - } - } - val parseInvoice: Route = postRequest("parseinvoice") { implicit t => formFields(invoiceFormParam) { invoice => complete(invoice) @@ -77,6 +69,6 @@ trait Invoice { } } - val invoiceRoutes: Route = createInvoice ~ getInvoice ~ listInvoices ~ listPendingInvoices ~ listExpiredInvoices ~ parseInvoice ~ deleteInvoice + val invoiceRoutes: Route = createInvoice ~ getInvoice ~ listInvoices ~ listPendingInvoices ~ parseInvoice ~ deleteInvoice } From 4eab4e6eff6300c2099dc1ff1b4f6f632c7f11fc Mon Sep 17 00:00:00 2001 From: rorp Date: Sun, 2 Apr 2023 08:44:47 -0700 Subject: [PATCH 5/5] update the release notes --- contrib/eclair-cli.bash-completion | 2 +- docs/release-notes/eclair-vnext.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/eclair-cli.bash-completion b/contrib/eclair-cli.bash-completion index 6f1b660789..47b76ecd76 100644 --- a/contrib/eclair-cli.bash-completion +++ b/contrib/eclair-cli.bash-completion @@ -21,7 +21,7 @@ _eclair-cli() *) # works fine, but is too slow at the moment. # allopts=$($eclaircli help 2>&1 | awk '$1 ~ /^"/ { sub(/,/, ""); print $1}' | sed 's/[":]//g') - allopts="getinfo connect open close forceclose updaterelayfee peers channels channel allnodes allchannels allupdates findroute findroutetonode findroutebetweennodes parseinvoice payinvoice sendtonode getsentinfo createinvoice getinvoice listinvoices listpendinginvoices listpaidinvoices listexpiredinvoices getreceivedinfo audit networkfees channelstats" + allopts="getinfo connect open close forceclose updaterelayfee peers channels channel allnodes allchannels allupdates findroute findroutetonode findroutebetweennodes parseinvoice payinvoice sendtonode getsentinfo createinvoice getinvoice listinvoices listpendinginvoices listreceivedpayments getreceivedinfo audit networkfees channelstats" if ! [[ " $allopts " =~ " $prev " ]]; then # prevent double arguments if [[ -z "$cur" || "$cur" =~ ^[a-z] ]]; then diff --git a/docs/release-notes/eclair-vnext.md b/docs/release-notes/eclair-vnext.md index 6db9601528..0f87e437cb 100644 --- a/docs/release-notes/eclair-vnext.md +++ b/docs/release-notes/eclair-vnext.md @@ -26,7 +26,7 @@ Offers are still experimental and some details could still change before they ar - `channel-created` is a new websocket event that is published when a channel's funding transaction has been broadcast (#2567) - `channel-opened` websocket event was updated to contain the final `channel_id` and be published when a channel is ready to process payments (#2567) - `getsentinfo` can now be used with `--offer` to list payments sent to a specific offer. -- `listreceivedpayments` allos to query received paid (#2607) +- `listreceivedpayments` lists payments received by your node (#2607) ### Miscellaneous improvements and bug fixes