@@ -33,7 +33,6 @@ import java.sql.{Statement, Timestamp}
33
33
import java .time .Instant
34
34
import java .util .UUID
35
35
import javax .sql .DataSource
36
- import scala .collection .immutable .Queue
37
36
38
37
class PgAuditDb (implicit ds : DataSource ) extends AuditDb with Logging {
39
38
@@ -215,30 +214,28 @@ class PgAuditDb(implicit ds: DataSource) extends AuditDb with Logging {
215
214
using(pg.prepareStatement(" SELECT * FROM sent WHERE timestamp BETWEEN ? AND ?" )) { statement =>
216
215
statement.setTimestamp(1 , Timestamp .from(Instant .ofEpochMilli(from)))
217
216
statement.setTimestamp(2 , Timestamp .from(Instant .ofEpochMilli(to)))
218
- val rs = statement.executeQuery()
219
- var sentByParentId = Map .empty[UUID , PaymentSent ]
220
- while (rs.next()) {
221
- val parentId = UUID .fromString(rs.getString(" parent_payment_id" ))
222
- val part = PaymentSent .PartialPayment (
223
- UUID .fromString(rs.getString(" payment_id" )),
224
- MilliSatoshi (rs.getLong(" amount_msat" )),
225
- MilliSatoshi (rs.getLong(" fees_msat" )),
226
- rs.getByteVector32FromHex(" to_channel_id" ),
227
- None , // we don't store the route in the audit DB
228
- rs.getTimestamp(" timestamp" ).getTime)
229
- val sent = sentByParentId.get(parentId) match {
230
- case Some (s) => s.copy(parts = s.parts :+ part)
231
- case None => PaymentSent (
232
- parentId,
233
- rs.getByteVector32FromHex(" payment_hash" ),
234
- rs.getByteVector32FromHex(" payment_preimage" ),
235
- MilliSatoshi (rs.getLong(" recipient_amount_msat" )),
236
- PublicKey (rs.getByteVectorFromHex(" recipient_node_id" )),
237
- Seq (part))
238
- }
239
- sentByParentId = sentByParentId + (parentId -> sent)
240
- }
241
- sentByParentId.values.toSeq.sortBy(_.timestamp)
217
+ statement.executeQuery()
218
+ .foldLeft(Map .empty[UUID , PaymentSent ]) { (sentByParentId, rs) =>
219
+ val parentId = UUID .fromString(rs.getString(" parent_payment_id" ))
220
+ val part = PaymentSent .PartialPayment (
221
+ UUID .fromString(rs.getString(" payment_id" )),
222
+ MilliSatoshi (rs.getLong(" amount_msat" )),
223
+ MilliSatoshi (rs.getLong(" fees_msat" )),
224
+ rs.getByteVector32FromHex(" to_channel_id" ),
225
+ None , // we don't store the route in the audit DB
226
+ rs.getTimestamp(" timestamp" ).getTime)
227
+ val sent = sentByParentId.get(parentId) match {
228
+ case Some (s) => s.copy(parts = s.parts :+ part)
229
+ case None => PaymentSent (
230
+ parentId,
231
+ rs.getByteVector32FromHex(" payment_hash" ),
232
+ rs.getByteVector32FromHex(" payment_preimage" ),
233
+ MilliSatoshi (rs.getLong(" recipient_amount_msat" )),
234
+ PublicKey (rs.getByteVectorFromHex(" recipient_node_id" )),
235
+ Seq (part))
236
+ }
237
+ sentByParentId + (parentId -> sent)
238
+ }.values.toSeq.sortBy(_.timestamp)
242
239
}
243
240
}
244
241
@@ -247,98 +244,91 @@ class PgAuditDb(implicit ds: DataSource) extends AuditDb with Logging {
247
244
using(pg.prepareStatement(" SELECT * FROM received WHERE timestamp BETWEEN ? AND ?" )) { statement =>
248
245
statement.setTimestamp(1 , Timestamp .from(Instant .ofEpochMilli(from)))
249
246
statement.setTimestamp(2 , Timestamp .from(Instant .ofEpochMilli(to)))
250
- val rs = statement.executeQuery()
251
- var receivedByHash = Map .empty[ByteVector32 , PaymentReceived ]
252
- while (rs.next()) {
253
- val paymentHash = rs.getByteVector32FromHex(" payment_hash" )
254
- val part = PaymentReceived .PartialPayment (
255
- MilliSatoshi (rs.getLong(" amount_msat" )),
256
- rs.getByteVector32FromHex(" from_channel_id" ),
257
- rs.getTimestamp(" timestamp" ).getTime)
258
- val received = receivedByHash.get(paymentHash) match {
259
- case Some (r) => r.copy(parts = r.parts :+ part)
260
- case None => PaymentReceived (paymentHash, Seq (part))
261
- }
262
- receivedByHash = receivedByHash + (paymentHash -> received)
263
- }
264
- receivedByHash.values.toSeq.sortBy(_.timestamp)
247
+ statement.executeQuery()
248
+ .foldLeft(Map .empty[ByteVector32 , PaymentReceived ]) { (receivedByHash, rs) =>
249
+ val paymentHash = rs.getByteVector32FromHex(" payment_hash" )
250
+ val part = PaymentReceived .PartialPayment (
251
+ MilliSatoshi (rs.getLong(" amount_msat" )),
252
+ rs.getByteVector32FromHex(" from_channel_id" ),
253
+ rs.getTimestamp(" timestamp" ).getTime)
254
+ val received = receivedByHash.get(paymentHash) match {
255
+ case Some (r) => r.copy(parts = r.parts :+ part)
256
+ case None => PaymentReceived (paymentHash, Seq (part))
257
+ }
258
+ receivedByHash + (paymentHash -> received)
259
+ }.values.toSeq.sortBy(_.timestamp)
265
260
}
266
261
}
267
262
268
263
override def listRelayed (from : Long , to : Long ): Seq [PaymentRelayed ] =
269
264
inTransaction { pg =>
270
- var trampolineByHash = Map .empty[ByteVector32 , (MilliSatoshi , PublicKey )]
271
- using(pg.prepareStatement(" SELECT * FROM relayed_trampoline WHERE timestamp BETWEEN ? and ?" )) { statement =>
265
+ val trampolineByHash = using(pg.prepareStatement(" SELECT * FROM relayed_trampoline WHERE timestamp BETWEEN ? and ?" )) { statement =>
272
266
statement.setTimestamp(1 , Timestamp .from(Instant .ofEpochMilli(from)))
273
267
statement.setTimestamp(2 , Timestamp .from(Instant .ofEpochMilli(to)))
274
- val rs = statement.executeQuery()
275
- while (rs.next() ) {
276
- val paymentHash = rs.getByteVector32FromHex(" payment_hash" )
277
- val amount = MilliSatoshi (rs.getLong(" amount_msat" ))
278
- val nodeId = PublicKey (rs.getByteVectorFromHex(" next_node_id" ))
279
- trampolineByHash += (paymentHash -> (amount, nodeId))
280
- }
268
+ statement.executeQuery()
269
+ .foldLeft( Map .empty[ ByteVector32 , ( MilliSatoshi , PublicKey )] ) { (trampolineByHash, rs) =>
270
+ val paymentHash = rs.getByteVector32FromHex(" payment_hash" )
271
+ val amount = MilliSatoshi (rs.getLong(" amount_msat" ))
272
+ val nodeId = PublicKey (rs.getByteVectorFromHex(" next_node_id" ))
273
+ trampolineByHash + (paymentHash -> (amount, nodeId))
274
+ }
281
275
}
282
- using(pg.prepareStatement(" SELECT * FROM relayed WHERE timestamp BETWEEN ? and ?" )) { statement =>
276
+ val relayedByHash = using(pg.prepareStatement(" SELECT * FROM relayed WHERE timestamp BETWEEN ? and ?" )) { statement =>
283
277
statement.setTimestamp(1 , Timestamp .from(Instant .ofEpochMilli(from)))
284
278
statement.setTimestamp(2 , Timestamp .from(Instant .ofEpochMilli(to)))
285
- val rs = statement.executeQuery()
286
- var relayedByHash = Map .empty[ByteVector32 , Seq [RelayedPart ]]
287
- while (rs.next()) {
288
- val paymentHash = rs.getByteVector32FromHex(" payment_hash" )
289
- val part = RelayedPart (
290
- rs.getByteVector32FromHex(" channel_id" ),
291
- MilliSatoshi (rs.getLong(" amount_msat" )),
292
- rs.getString(" direction" ),
293
- rs.getString(" relay_type" ),
294
- rs.getTimestamp(" timestamp" ).getTime)
295
- relayedByHash = relayedByHash + (paymentHash -> (relayedByHash.getOrElse(paymentHash, Nil ) :+ part))
296
- }
297
- relayedByHash.flatMap {
298
- case (paymentHash, parts) =>
299
- // We may have been routing multiple payments for the same payment_hash (MPP) in both cases (trampoline and channel).
300
- // NB: we may link the wrong in-out parts, but the overall sum will be correct: we sort by amounts to minimize the risk of mismatch.
301
- val incoming = parts.filter(_.direction == " IN" ).map(p => PaymentRelayed .Part (p.amount, p.channelId)).sortBy(_.amount)
302
- val outgoing = parts.filter(_.direction == " OUT" ).map(p => PaymentRelayed .Part (p.amount, p.channelId)).sortBy(_.amount)
303
- parts.headOption match {
304
- case Some (RelayedPart (_, _, _, " channel" , timestamp)) => incoming.zip(outgoing).map {
305
- case (in, out) => ChannelPaymentRelayed (in.amount, out.amount, paymentHash, in.channelId, out.channelId, timestamp)
306
- }
307
- case Some (RelayedPart (_, _, _, " trampoline" , timestamp)) =>
308
- val (nextTrampolineAmount, nextTrampolineNodeId) = trampolineByHash.getOrElse(paymentHash, (0 msat, PlaceHolderPubKey ))
309
- TrampolinePaymentRelayed (paymentHash, incoming, outgoing, nextTrampolineNodeId, nextTrampolineAmount, timestamp) :: Nil
310
- case _ => Nil
311
- }
312
- }.toSeq.sortBy(_.timestamp)
279
+ statement.executeQuery()
280
+ .foldLeft(Map .empty[ByteVector32 , Seq [RelayedPart ]]) { (relayedByHash, rs) =>
281
+ val paymentHash = rs.getByteVector32FromHex(" payment_hash" )
282
+ val part = RelayedPart (
283
+ rs.getByteVector32FromHex(" channel_id" ),
284
+ MilliSatoshi (rs.getLong(" amount_msat" )),
285
+ rs.getString(" direction" ),
286
+ rs.getString(" relay_type" ),
287
+ rs.getTimestamp(" timestamp" ).getTime)
288
+ relayedByHash + (paymentHash -> (relayedByHash.getOrElse(paymentHash, Nil ) :+ part))
289
+ }
313
290
}
291
+ relayedByHash.flatMap {
292
+ case (paymentHash, parts) =>
293
+ // We may have been routing multiple payments for the same payment_hash (MPP) in both cases (trampoline and channel).
294
+ // NB: we may link the wrong in-out parts, but the overall sum will be correct: we sort by amounts to minimize the risk of mismatch.
295
+ val incoming = parts.filter(_.direction == " IN" ).map(p => PaymentRelayed .Part (p.amount, p.channelId)).sortBy(_.amount)
296
+ val outgoing = parts.filter(_.direction == " OUT" ).map(p => PaymentRelayed .Part (p.amount, p.channelId)).sortBy(_.amount)
297
+ parts.headOption match {
298
+ case Some (RelayedPart (_, _, _, " channel" , timestamp)) => incoming.zip(outgoing).map {
299
+ case (in, out) => ChannelPaymentRelayed (in.amount, out.amount, paymentHash, in.channelId, out.channelId, timestamp)
300
+ }
301
+ case Some (RelayedPart (_, _, _, " trampoline" , timestamp)) =>
302
+ val (nextTrampolineAmount, nextTrampolineNodeId) = trampolineByHash.getOrElse(paymentHash, (0 msat, PlaceHolderPubKey ))
303
+ TrampolinePaymentRelayed (paymentHash, incoming, outgoing, nextTrampolineNodeId, nextTrampolineAmount, timestamp) :: Nil
304
+ case _ => Nil
305
+ }
306
+ }.toSeq.sortBy(_.timestamp)
314
307
}
315
308
316
309
override def listNetworkFees (from : Long , to : Long ): Seq [NetworkFee ] =
317
310
inTransaction { pg =>
318
311
using(pg.prepareStatement(" SELECT * FROM network_fees WHERE timestamp BETWEEN ? and ? ORDER BY timestamp" )) { statement =>
319
312
statement.setTimestamp(1 , Timestamp .from(Instant .ofEpochMilli(from)))
320
313
statement.setTimestamp(2 , Timestamp .from(Instant .ofEpochMilli(to)))
321
- val rs = statement.executeQuery()
322
- var q : Queue [NetworkFee ] = Queue ()
323
- while (rs.next()) {
324
- q = q :+ NetworkFee (
314
+ statement.executeQuery().map { rs =>
315
+ NetworkFee (
325
316
remoteNodeId = PublicKey (rs.getByteVectorFromHex(" node_id" )),
326
317
channelId = rs.getByteVector32FromHex(" channel_id" ),
327
318
txId = rs.getByteVector32FromHex(" tx_id" ),
328
319
fee = Satoshi (rs.getLong(" fee_sat" )),
329
320
txType = rs.getString(" tx_type" ),
330
321
timestamp = rs.getTimestamp(" timestamp" ).getTime)
331
- }
332
- q
322
+ }.toSeq
333
323
}
334
324
}
335
325
336
326
override def stats (from : Long , to : Long ): Seq [Stats ] = {
337
- val networkFees = listNetworkFees(from, to).foldLeft(Map .empty[ByteVector32 , Satoshi ]) { case (feeByChannelId, f) =>
327
+ val networkFees = listNetworkFees(from, to).foldLeft(Map .empty[ByteVector32 , Satoshi ]) { (feeByChannelId, f) =>
338
328
feeByChannelId + (f.channelId -> (feeByChannelId.getOrElse(f.channelId, 0 sat) + f.fee))
339
329
}
340
330
case class Relayed (amount : MilliSatoshi , fee : MilliSatoshi , direction : String )
341
- val relayed = listRelayed(from, to).foldLeft(Map .empty[ByteVector32 , Seq [Relayed ]]) { case (previous, e) =>
331
+ val relayed = listRelayed(from, to).foldLeft(Map .empty[ByteVector32 , Seq [Relayed ]]) { (previous, e) =>
342
332
// NB: we must avoid counting the fee twice: we associate it to the outgoing channels rather than the incoming ones.
343
333
val current = e match {
344
334
case c : ChannelPaymentRelayed => Map (
0 commit comments