diff --git a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/api/EclairApi.scala b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/api/EclairApi.scala index f0f9903eb5..8bd522e41a 100644 --- a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/api/EclairApi.scala +++ b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/api/EclairApi.scala @@ -19,6 +19,18 @@ trait EclairApi { def allNodes(): Future[Vector[NodeInfo]] + /** + * List all sent/received/relayed payments + */ + def audit(): Future[AuditResult] + + /** + * List all sent/received/relayed payments in the given interval + * @param from start timestamp + * @param to end timestamp + */ + def audit(from: Long, to: Long): Future[AuditResult] + def allUpdates(): Future[Vector[ChannelUpdate]] def allUpdates(nodeId: NodeId): Future[Vector[ChannelUpdate]] diff --git a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/client/EclairRpcClient.scala b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/client/EclairRpcClient.scala index 87bd6db2e1..e2fcfabe2f 100644 --- a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/client/EclairRpcClient.scala +++ b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/client/EclairRpcClient.scala @@ -59,6 +59,18 @@ class EclairRpcClient(val instance: EclairInstance)( eclairCall[Vector[ChannelUpdate]]("allupdates", List(JsString(nodeId.toString))) + /** + * @inheritdoc + */ + override def audit(): Future[AuditResult] = + eclairCall[AuditResult]("audit", List.empty) + + /** + * @inheritdoc + */ + override def audit(from: Long, to: Long): Future[AuditResult] = + eclairCall[AuditResult]("audit", List(JsNumber(from), JsNumber(to))) + override def channel(channelId: ChannelId): Future[ChannelResult] = { eclairCall[ChannelResult]("channel", List(JsString(channelId.hex))) } diff --git a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/json/EclairModels.scala b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/json/EclairModels.scala index 47b4aeb0ee..b21570bd61 100644 --- a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/json/EclairModels.scala +++ b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/json/EclairModels.scala @@ -86,6 +86,37 @@ case class NodeInfo( case class ChannelDesc(shortChannelId: ShortChannelId, a: NodeId, b: NodeId) +case class AuditResult( + sent: Vector[SentPayment], + relayed: Vector[RelayedPayment], + received: Vector[ReceivedPayment] +) + +case class ReceivedPayment( + amount: MilliSatoshis, + paymentHash: Sha256Digest, + fromChannelId: FundedChannelId, + timestamp: Long +) + +case class RelayedPayment( + amountIn: MilliSatoshis, + amountOut: MilliSatoshis, + paymentHash: Sha256Digest, + fromChannelId: FundedChannelId, + toChannelId: FundedChannelId, + timestamp: Long +) + +case class SentPayment( + amount: MilliSatoshis, + feesPaid: MilliSatoshis, + paymentHash: Sha256Digest, + paymentPreimage: String, + toChannelId: FundedChannelId, + timestamp: Long +) + case class ChannelUpdate( signature: ECDigitalSignature, chainHash: DoubleSha256Digest, diff --git a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/json/JsonReaders.scala b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/json/JsonReaders.scala index 48f886eef8..32459bc3c2 100644 --- a/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/json/JsonReaders.scala +++ b/eclair-rpc/src/main/scala/org/bitcoins/eclair/rpc/json/JsonReaders.scala @@ -222,4 +222,11 @@ object JsonReaders { JsNull) => JsError(s"Invalid type on refund invoice: $bad, expected JsString") } + + implicit val receivedPaymentReads: Reads[ReceivedPayment] = + Json.reads[ReceivedPayment] + implicit val sentPaymentReads: Reads[SentPayment] = Json.reads[SentPayment] + implicit val relayedPaymentReads: Reads[RelayedPayment] = + Json.reads[RelayedPayment] + implicit val auditResultReads: Reads[AuditResult] = Json.reads[AuditResult] } diff --git a/eclair-rpc/src/test/scala/org/bitcoins/eclair/rpc/EclairRpcClientTest.scala b/eclair-rpc/src/test/scala/org/bitcoins/eclair/rpc/EclairRpcClientTest.scala index 1127f85129..561582f027 100644 --- a/eclair-rpc/src/test/scala/org/bitcoins/eclair/rpc/EclairRpcClientTest.scala +++ b/eclair-rpc/src/test/scala/org/bitcoins/eclair/rpc/EclairRpcClientTest.scala @@ -36,6 +36,14 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll { val bitcoindRpcClient: BitcoindRpcClient = BitcoindRpcTestUtil.startedBitcoindRpcClient() + lazy val EclairNodes4(firstClient, secondClient, thirdClient, fourthClient) = { + val EclairNodes4(first, second, third, fourth) = + EclairRpcTestUtil.createNodeLink(bitcoindRpcClient) + clients ++= List(first, second, third, fourth) + EclairNodes4(first, second, third, fourth) + + } + lazy val (client, otherClient) = { val (c1, c2) = EclairRpcTestUtil.createNodePair(Some(bitcoindRpcClient)) clients += c1 @@ -357,13 +365,9 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll { } it should "get a route to a node ID" in { - val EclairNodes4(first, second, third, fourth) = - EclairRpcTestUtil.createNodeLink(bitcoindRpcClient) - clients ++= List(first, second, third, fourth) - val hasRoute = () => { - fourth.getInfo - .flatMap(info => first.findRoute(info.nodeId)) + fourthClient.getInfo + .flatMap(info => firstClient.findRoute(info.nodeId)) .map(route => route.length == 4) .recover { case err: RuntimeException @@ -379,14 +383,10 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll { } it should "get a route to an invoice" in { - val EclairNodes4(first, second, third, fourth) = - EclairRpcTestUtil.createNodeLink(bitcoindRpcClient) - clients ++= List(first, second, third, fourth) - val hasRoute = () => { - fourth + fourthClient .receive("foo") - .flatMap(invoice => first.findRoute(invoice)) + .flatMap(invoice => firstClient.findRoute(invoice)) .map(route => route.length == 4) .recover { case err: RuntimeException @@ -401,6 +401,28 @@ class EclairRpcClientTest extends AsyncFlatSpec with BeforeAndAfterAll { succeed } + it should "send some payments and get the audit info" in { + for { + invoice <- fourthClient.receive(MilliSatoshis(50000).toLnCurrencyUnit) + _ <- firstClient + .send(invoice) + .map(payment => assert(payment.isInstanceOf[PaymentSucceeded])) + received <- fourthClient + .audit() + .map(_.received) // check for received payments + relayed <- secondClient + .audit() + .map(_.relayed) // check for relayed payments + sent <- firstClient + .audit() + .map(_.sent) // check for sent payments + } yield { + assert(received.nonEmpty) + assert(relayed.nonEmpty) + assert(sent.nonEmpty) + } + } + // We spawn fresh clients in this test because the test // needs nodes with activity both related and not related // to them diff --git a/project/Deps.scala b/project/Deps.scala index f2228521d0..3310299d2c 100644 --- a/project/Deps.scala +++ b/project/Deps.scala @@ -17,7 +17,7 @@ object Deps { val nativeLoaderV = "2.3.2" val typesafeConfigV = "1.3.3" - val bitcoinsV = "0.0.4.1-SNAPSHOT" + val bitcoinsV = "5d3bf4-1548681478579-SNAPSHOT" } object Compile { diff --git a/testkit/src/main/scala/org/bitcoins/eclair/rpc/EclairRpcTestUtil.scala b/testkit/src/main/scala/org/bitcoins/eclair/rpc/EclairRpcTestUtil.scala index bb40fe333d..8d6738f771 100644 --- a/testkit/src/main/scala/org/bitcoins/eclair/rpc/EclairRpcTestUtil.scala +++ b/testkit/src/main/scala/org/bitcoins/eclair/rpc/EclairRpcTestUtil.scala @@ -93,6 +93,7 @@ trait EclairRpcTestUtil extends BitcoinSLogger { "eclair.auto-reconnect" -> false, "eclair.db.driver" -> "org.sqlite.JDBC", "eclair.db.regtest.url" -> "jdbc:sqlite:regtest/", + "eclair.max-payment-fee" -> 10, // avoid complaints about too high fees "eclair.alias" -> "suredbits" ) }