-
Notifications
You must be signed in to change notification settings - Fork 267
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move Peerswap functionality into a plugin
- Loading branch information
Showing
36 changed files
with
894 additions
and
331 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Peerswap plugin | ||
|
||
This plugin allows implements the PeerSwap protocol: https://github.com/ElementsProject/peerswap-spec/blob/main/peer-protocol.md | ||
|
||
## Build | ||
|
||
To build this plugin, run the following command in this directory: | ||
|
||
```sh | ||
mvn package | ||
``` | ||
|
||
## Run | ||
|
||
To run eclair with this plugin, start eclair with the following command: | ||
|
||
```sh | ||
eclair-node-<version>/bin/eclair-node.sh <path-to-plugin-jar>/peerswap-plugin-<version>.jar | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!-- | ||
~ Copyright 2022 ACINQ SAS | ||
~ | ||
~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
~ you may not use this file except in compliance with the License. | ||
~ You may obtain a copy of the License at | ||
~ | ||
~ http://www.apache.org/licenses/LICENSE-2.0 | ||
~ | ||
~ Unless required by applicable law or agreed to in writing, software | ||
~ distributed under the License is distributed on an "AS IS" BASIS, | ||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
~ See the License for the specific language governing permissions and | ||
~ limitations under the License. | ||
--> | ||
|
||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>fr.acinq.eclair</groupId> | ||
<artifactId>eclair_2.13</artifactId> | ||
<version>0.7.1-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>peerswap-plugin_2.13</artifactId> | ||
<packaging>jar</packaging> | ||
<name>peerswap-plugin</name> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>com.googlecode.maven-download-plugin</groupId> | ||
<artifactId>download-maven-plugin</artifactId> | ||
<version>1.3.0</version> | ||
<executions> | ||
<execution> | ||
<id>download-bitcoind</id> | ||
<phase>generate-test-resources</phase> | ||
<goals> | ||
<goal>wget</goal> | ||
</goals> | ||
<configuration> | ||
<skip>${maven.test.skip}</skip> | ||
<url>${bitcoind.url}</url> | ||
<unpack>true</unpack> | ||
<outputDirectory>${project.build.directory}</outputDirectory> | ||
<md5>${bitcoind.md5}</md5> | ||
<sha1>${bitcoind.sha1}</sha1> | ||
</configuration> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-shade-plugin</artifactId> | ||
<version>3.2.1</version> | ||
<configuration> | ||
<transformers> | ||
<!-- Add a manifest entry for Main-Class with the FQDN of the implementation. --> | ||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> | ||
<manifestEntries> | ||
<Main-Class>fr.acinq.eclair.plugins.peerswap.PeerSwapPlugin</Main-Class> | ||
</manifestEntries> | ||
</transformer> | ||
</transformers> | ||
</configuration> | ||
<executions> | ||
<execution> | ||
<phase>package</phase> | ||
<goals> | ||
<goal>shade</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
<profiles> | ||
<profile> | ||
<id>default</id> | ||
<activation> | ||
<activeByDefault>true</activeByDefault> | ||
</activation> | ||
<properties> | ||
<bitcoind.url>https://bitcoincore.org/bin/bitcoin-core-0.21.1/bitcoin-0.21.1-x86_64-linux-gnu.tar.gz</bitcoind.url> | ||
<bitcoind.md5>e283a98b5e9f0b58e625e1dde661201d</bitcoind.md5> | ||
<bitcoind.sha1>5101e29b39c33cc8e40d5f3b46dda37991b037a0</bitcoind.sha1> | ||
</properties> | ||
</profile> | ||
<profile> | ||
<id>Mac</id> | ||
<activation> | ||
<os> | ||
<family>mac</family> | ||
</os> | ||
</activation> | ||
<properties> | ||
<bitcoind.url>https://bitcoincore.org/bin/bitcoin-core-0.21.1/bitcoin-0.21.1-osx64.tar.gz</bitcoind.url> | ||
<bitcoind.md5>dfd1f323678eede14ae2cf6afb26ff6a</bitcoind.md5> | ||
<bitcoind.sha1>4273696f90a2648f90142438221f5d1ade16afa2</bitcoind.sha1> | ||
</properties> | ||
</profile> | ||
<profile> | ||
<id>Windows</id> | ||
<activation> | ||
<os> | ||
<family>Windows</family> | ||
</os> | ||
</activation> | ||
<properties> | ||
<bitcoind.url>https://bitcoincore.org/bin/bitcoin-core-0.21.1/bitcoin-0.21.1-win64.zip</bitcoind.url> | ||
<bitcoind.md5>1c6f5081ea68dcec7eddb9e6cdfc508d</bitcoind.md5> | ||
<bitcoind.sha1>a782cd413fc736f05fad3831d6a9f59dde779520</bitcoind.sha1> | ||
</properties> | ||
</profile> | ||
</profiles> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.scala-lang</groupId> | ||
<artifactId>scala-library</artifactId> | ||
<version>${scala.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>fr.acinq.eclair</groupId> | ||
<artifactId>eclair-core_${scala.version.short}</artifactId> | ||
<version>${project.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>fr.acinq.eclair</groupId> | ||
<artifactId>eclair-node_${scala.version.short}</artifactId> | ||
<version>${project.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<!-- TESTS --> | ||
<dependency> | ||
<groupId>com.typesafe.akka</groupId> | ||
<artifactId>akka-testkit_${scala.version.short}</artifactId> | ||
<version>${akka.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.typesafe.akka</groupId> | ||
<artifactId>akka-actor-testkit-typed_${scala.version.short}</artifactId> | ||
<version>${akka.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>fr.acinq.eclair</groupId> | ||
<artifactId>eclair-core_${scala.version.short}</artifactId> | ||
<version>${project.version}</version> | ||
<classifier>tests</classifier> | ||
<type>test-jar</type> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
66 changes: 66 additions & 0 deletions
66
plugins/peerswap/src/main/scala/fr/acinq/eclair/plugins/peerswap/ApiHandlers.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
* Copyright 2022 ACINQ SAS | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package fr.acinq.eclair.plugins.peerswap | ||
|
||
import akka.http.scaladsl.common.{NameReceptacle, NameUnmarshallerReceptacle} | ||
import akka.http.scaladsl.server.Route | ||
import fr.acinq.bitcoin.scalacompat.{ByteVector32, Satoshi} | ||
import fr.acinq.eclair.api.directives.EclairDirectives | ||
import fr.acinq.eclair.api.serde.FormParamExtractors._ | ||
|
||
object ApiHandlers { | ||
|
||
import fr.acinq.eclair.api.serde.JsonSupport.{marshaller, serialization} | ||
import fr.acinq.eclair.plugins.peerswap.json.PeerSwapJsonSerializers.formats | ||
|
||
def registerRoutes(kit: PeerSwapKit, eclairDirectives: EclairDirectives): Route = { | ||
import eclairDirectives._ | ||
|
||
val swapIdFormParam: NameUnmarshallerReceptacle[ByteVector32] = "swapId".as[ByteVector32](sha256HashUnmarshaller) | ||
|
||
val amountSatFormParam: NameReceptacle[Satoshi] = "amountSat".as[Satoshi] | ||
|
||
val swapIn: Route = postRequest("swapin") { implicit t => | ||
formFields(shortChannelIdFormParam, amountSatFormParam) { (channelId, amount) => | ||
complete(kit.swapIn(channelId, amount)) | ||
} | ||
} | ||
|
||
val swapOut: Route = postRequest("swapout") { implicit t => | ||
formFields(shortChannelIdFormParam, amountSatFormParam) { (channelId, amount) => | ||
complete(kit.swapOut(channelId, amount)) | ||
} | ||
} | ||
|
||
val listSwaps: Route = postRequest("listswaps") { implicit t => | ||
complete(kit.listSwaps()) | ||
} | ||
|
||
val cancelSwap: Route = postRequest("cancelswap") { implicit t => | ||
formFields(swapIdFormParam) { swapId => | ||
complete(kit.cancelSwap(swapId.toString())) | ||
} | ||
} | ||
|
||
val peerSwapRoutes: Route = swapIn ~ swapOut ~ listSwaps ~ cancelSwap | ||
|
||
peerSwapRoutes | ||
} | ||
|
||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
plugins/peerswap/src/main/scala/fr/acinq/eclair/plugins/peerswap/PeerSwapPlugin.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
* Copyright 2022 ACINQ SAS | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package fr.acinq.eclair.plugins.peerswap | ||
|
||
import akka.actor.ActorSystem | ||
import akka.actor.typed.scaladsl.AskPattern.Askable | ||
import akka.actor.typed.scaladsl.Behaviors | ||
import akka.actor.typed.scaladsl.adapter.{ClassicActorSystemOps, ClassicSchedulerOps} | ||
import akka.actor.typed.{ActorRef, SupervisorStrategy} | ||
import akka.http.scaladsl.server.Route | ||
import akka.util.Timeout | ||
import fr.acinq.bitcoin.scalacompat.Satoshi | ||
import fr.acinq.eclair.api.directives.EclairDirectives | ||
import fr.acinq.eclair.db.sqlite.SqliteUtils | ||
import fr.acinq.eclair.plugins.peerswap.SwapResponses.{Response, Status} | ||
import fr.acinq.eclair.plugins.peerswap.db.SwapsDb | ||
import fr.acinq.eclair.plugins.peerswap.db.sqlite.SqliteSwapsDb | ||
import fr.acinq.eclair.{CustomFeaturePlugin, Feature, InitFeature, Kit, NodeFeature, NodeParams, Plugin, PluginParams, RouteProvider, Setup, ShortChannelId} | ||
import grizzled.slf4j.Logging | ||
import scodec.bits.ByteVector | ||
|
||
import java.io.File | ||
import java.nio.file.Files | ||
import scala.concurrent.Future | ||
|
||
/** | ||
* This plugin implements the PeerSwap protocol: https://github.com/ElementsProject/peerswap-spec/blob/main/peer-protocol.md | ||
*/ | ||
object PeerSwapPlugin { | ||
// TODO: derive this set from peerSwapMessageCodec tags | ||
val peerSwapTags: Set[Int] = Set(42069, 42071, 42073, 42075, 42077, 42079, 42081) | ||
} | ||
|
||
class PeerSwapPlugin extends Plugin with RouteProvider with Logging { | ||
|
||
var db: SwapsDb = _ | ||
var swapKeyManager: LocalSwapKeyManager = _ | ||
var pluginKit: PeerSwapKit = _ | ||
|
||
case object PeerSwapFeature extends Feature with InitFeature with NodeFeature { | ||
val rfcName = "peer_swap_plugin_prototype" | ||
val mandatory = 158 | ||
} | ||
|
||
override def params: PluginParams = new CustomFeaturePlugin { | ||
// @formatter:off | ||
override def messageTags: Set[Int] = PeerSwapPlugin.peerSwapTags | ||
override def feature: Feature = PeerSwapFeature | ||
override def name: String = "PeerSwap" | ||
// @formatter:on | ||
} | ||
|
||
override def onSetup(setup: Setup): Unit = { | ||
val chain = setup.config.getString("chain") | ||
val chainDir = new File(setup.datadir, chain) | ||
db = new SqliteSwapsDb(SqliteUtils.openSqliteFile(chainDir, "peer-swap.sqlite", exclusiveLock = false, journalMode = "wal", syncFlag = "normal")) | ||
|
||
// load seed | ||
val seedFilename: String = "swap_seed.dat" | ||
val seedPath: File = new File(setup.datadir, seedFilename) | ||
val swapSeed: ByteVector = ByteVector(Files.readAllBytes(seedPath.toPath)) | ||
swapKeyManager = new LocalSwapKeyManager(swapSeed, NodeParams.hashFromChain(chain)) | ||
} | ||
|
||
override def onKit(kit: Kit): Unit = { | ||
val data = db.restore().toSet | ||
val swapRegister = kit.system.spawn(Behaviors.supervise(SwapRegister(kit.nodeParams, kit.paymentInitiator, kit.watcher, kit.register, kit.wallet, swapKeyManager, db, data)).onFailure(SupervisorStrategy.restart), "peerswap-plugin-swap-register") | ||
pluginKit = PeerSwapKit(kit.nodeParams, kit.system, swapRegister) | ||
} | ||
|
||
override def route(eclairDirectives: EclairDirectives): Route = ApiHandlers.registerRoutes(pluginKit, eclairDirectives) | ||
|
||
} | ||
|
||
case class PeerSwapKit(nodeParams: NodeParams, system: ActorSystem, swapRegister: ActorRef[SwapRegister.Command]) { | ||
def swapIn(shortChannelId: ShortChannelId, amount: Satoshi)(implicit timeout: Timeout): Future[Response] = | ||
swapRegister.ask(ref => SwapRegister.SwapInRequested(ref, amount, shortChannelId))(timeout, system.scheduler.toTyped) | ||
|
||
def swapOut(shortChannelId: ShortChannelId, amount: Satoshi)(implicit timeout: Timeout): Future[Response] = | ||
swapRegister.ask(ref => SwapRegister.SwapOutRequested(ref, amount, shortChannelId))(timeout, system.scheduler.toTyped) | ||
|
||
def listSwaps()(implicit timeout: Timeout): Future[Iterable[Status]] = | ||
swapRegister.ask(ref => SwapRegister.ListPendingSwaps(ref))(timeout, system.scheduler.toTyped) | ||
|
||
def cancelSwap(swapId: String)(implicit timeout: Timeout): Future[Response] = | ||
swapRegister.ask(ref => SwapRegister.CancelSwapRequested(ref, swapId))(timeout, system.scheduler.toTyped) | ||
} |
Oops, something went wrong.