Skip to content

Commit

Permalink
Add API for canceling an async payment waiting to be relayed
Browse files Browse the repository at this point in the history
  • Loading branch information
remyers committed Dec 29, 2022
1 parent df11a7b commit 0c7b049
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 4 deletions.
8 changes: 8 additions & 0 deletions eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import fr.acinq.eclair.io._
import fr.acinq.eclair.message.{OnionMessages, Postman}
import fr.acinq.eclair.payment._
import fr.acinq.eclair.payment.receive.MultiPartHandler.ReceiveStandardPayment
import fr.acinq.eclair.payment.relay.AsyncPaymentTriggerer.Cancel
import fr.acinq.eclair.payment.relay.Relayer.{ChannelBalance, GetOutgoingChannels, OutgoingChannels, RelayFees}
import fr.acinq.eclair.payment.send.ClearRecipient
import fr.acinq.eclair.payment.send.PaymentInitiator._
Expand Down Expand Up @@ -164,6 +165,8 @@ trait Eclair {
def sendOnionMessage(intermediateNodes: Seq[PublicKey], destination: Either[PublicKey, Sphinx.RouteBlinding.BlindedRoute], replyPath: Option[Seq[PublicKey]], userCustomContent: ByteVector)(implicit timeout: Timeout): Future[SendOnionMessageResponse]

def stop(): Future[Unit]

def cancelAsyncPayment(paymentHash: ByteVector32)(implicit timeout: Timeout): Future[Unit]
}

class EclairImpl(appKit: Kit) extends Eclair with Logging {
Expand Down Expand Up @@ -587,4 +590,9 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging {
sys.exit(0)
Future.successful(())
}

override def cancelAsyncPayment(paymentHash: ByteVector32)(implicit timeout: Timeout): Future[Unit] = {
appKit.triggerer ! Cancel(paymentHash)
Future.successful(())
}
}
6 changes: 4 additions & 2 deletions eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,8 @@ class Setup(val datadir: File,
channelsListener = channelsListener,
balanceActor = balanceActor,
postman = postman,
wallet = bitcoinClient)
wallet = bitcoinClient,
triggerer = triggerer)

zmqBlockTimeout = after(5 seconds, using = system.scheduler)(Future.failed(BitcoinZMQConnectionTimeoutException))
zmqTxTimeout = after(5 seconds, using = system.scheduler)(Future.failed(BitcoinZMQConnectionTimeoutException))
Expand Down Expand Up @@ -399,7 +400,8 @@ case class Kit(nodeParams: NodeParams,
channelsListener: typed.ActorRef[ChannelsListener.Command],
balanceActor: typed.ActorRef[BalanceActor.Command],
postman: typed.ActorRef[Postman.Command],
wallet: OnChainWallet)
wallet: OnChainWallet,
triggerer: typed.ActorRef[AsyncPaymentTriggerer.Command])

object Kit {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class EclairImplSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with I
val channelsListener = TestProbe()
val balanceActor = TestProbe()
val postman = TestProbe()
val triggerer = TestProbe()
val kit = Kit(
TestConstants.Alice.nodeParams,
system,
Expand All @@ -84,7 +85,8 @@ class EclairImplSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with I
channelsListener.ref.toTyped,
balanceActor.ref.toTyped,
postman.ref.toTyped,
new DummyOnChainWallet()
new DummyOnChainWallet(),
triggerer.ref.toTyped
)
withFixture(test.toNoArgTest(FixtureParam(register, relayer, router, paymentInitiator, switchboard, paymentHandler, TestProbe(), kit)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ trait Payment {
}
}

val paymentRoutes: Route = usableBalances ~ payInvoice ~ sendToNode ~ sendToRoute ~ getSentInfo ~ getReceivedInfo
val cancelAsyncPayment: Route = postRequest("cancelasyncpayment") { implicit t =>
formFields(paymentHashFormParam) { paymentHash =>
complete(eclairApi.cancelAsyncPayment(paymentHash))
}
}

val paymentRoutes: Route = usableBalances ~ payInvoice ~ sendToNode ~ sendToRoute ~ getSentInfo ~ getReceivedInfo ~ cancelAsyncPayment

}
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,21 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
}
}

test("cancelAsyncPayment") {
val eclair = mock[Eclair]
val mockService = new MockService(eclair)
val paymentHash = randomBytes32()
eclair.cancelAsyncPayment(paymentHash)(any) returns Future.successful(None)

Post("/cancelasyncpayment", FormData("paymentHash" -> paymentHash.toHex).toEntity) ~>
addCredentials(BasicHttpCredentials("", mockApi().password)) ~>
Route.seal(mockService.cancelAsyncPayment) ~>
check {
assert(handled)
assert(status == OK)
}
}

private def matchTestJson(apiName: String, response: String) = {
val resource = getClass.getResourceAsStream(s"/api/$apiName")
val expectedResponse = Try(Source.fromInputStream(resource).mkString).getOrElse {
Expand Down

0 comments on commit 0c7b049

Please sign in to comment.