Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/eclair-vnext.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### API changes

- `channelbalances` Retrieves information about the balances of all local channels. (#2196)
- `stop` Stops eclair. Please note that the recommended way of stopping eclair is simply to kill its process (#2233)

### Miscellaneous improvements and bug fixes

Expand Down
1 change: 1 addition & 0 deletions eclair-core/eclair-cli
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ and COMMAND is one of the available commands:
- disconnect
- peers
- audit
- stop

=== Channel ===
- open
Expand Down
10 changes: 10 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 @@ -159,6 +159,8 @@ trait Eclair {
def verifyMessage(message: ByteVector, recoverableSignature: ByteVector): VerifiedMessage

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]
}

class EclairImpl(appKit: Kit) extends Eclair with Logging {
Expand Down Expand Up @@ -559,4 +561,12 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging {
case Attempt.Failure(cause) => Future.successful(SendOnionMessageResponse(sent = false, failureMessage = Some(s"the `content` field is invalid, it must contain encoded tlvs: ${cause.message}"), response = None))
}
}

override def stop(): Future[Unit] = {
// README: do not make this smarter or more complex !
// eclair can simply and cleanly be stopped by killing its process without fear of losing data, payments, ... and it should remain this way.
logger.info("stopping eclair")
sys.exit(0)
Future.successful(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,9 @@ trait Node {
}
}

val nodeRoutes: Route = getInfo ~ connect ~ disconnect ~ peers ~ audit
val stop: Route = postRequest("stop") { implicit t =>
complete(eclairApi.stop())
}

val nodeRoutes: Route = getInfo ~ connect ~ disconnect ~ peers ~ audit ~ stop
}
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,20 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
}
}

test("stop eclair") {
val eclair = mock[Eclair]
val mockService = new MockService(eclair)
eclair.stop() returns Future.successful(())

Post("/stop") ~>
addCredentials(BasicHttpCredentials("", mockApi().password)) ~>
Route.seal(mockService.stop) ~>
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