Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added simple plugin support #927

Merged
merged 3 commits into from Apr 19, 2019
Merged
Changes from 2 commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -152,6 +152,17 @@ If you want to persist the data directory, you can make the volume to your host
docker run -ti --rm -v "/path_on_host:/data" -e "JAVA_OPTS=-Declair.printToConsole" acinq/eclair
```

## Plugins

For advanced usage, Eclair supports plugins written in Scala, Java, or any JVM-compatible language.

A valid plugin is a jar that contains an implementation of the [Plugin](eclair-node/src/main/scala/fr/acinq/eclair/Plugin.scala) interface.

Here is how to run Eclair with plugins:
```shell
java -jar eclair-node-<version>-<commit_id>.jar <plugin1.jar> <plugin2.jar> <...>
```

## Mainnet usage

Following are the minimum configuration files you need to use for Bitcoin Core and Eclair.
@@ -74,6 +74,12 @@
<artifactId>eclair-core_${scala.version.short}</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<!-- for plugins -->
<groupId>org.clapper</groupId>
<artifactId>classutil_${scala.version.short}</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
@@ -22,6 +22,7 @@ import akka.actor.ActorSystem
import grizzled.slf4j.Logging

import scala.concurrent.ExecutionContext
import scala.util.{Failure, Success}

/**
* Created by PM on 25/01/2016.
@@ -31,10 +32,15 @@ object Boot extends App with Logging {
val datadir = new File(System.getProperty("eclair.datadir", System.getProperty("user.home") + "/.eclair"))

try {
val plugins = Plugin.loadPlugins(args.map(new File(_)))
plugins.foreach(plugin => logger.info(s"loaded plugin ${plugin.getClass.getSimpleName}"))
implicit val system: ActorSystem = ActorSystem("eclair-node")
implicit val ec: ExecutionContext = system.dispatcher
new Setup(datadir).bootstrap onFailure {
case t: Throwable => onError(t)
val setup = new Setup(datadir)
plugins.foreach(_.onSetup(setup))
setup.bootstrap onComplete {
case Success(kit) => plugins.foreach(_.onKit(kit))
case Failure(t) => onError(t)
}
} catch {
case t: Throwable => onError(t)
@@ -47,4 +53,3 @@ object Boot extends App with Logging {
System.exit(1)
}
}

@@ -0,0 +1,29 @@
package fr.acinq.eclair

import java.io.File
import java.net.{URL, URLClassLoader}

import org.clapper.classutil.ClassFinder

trait Plugin {

def onSetup(setup: Setup): Unit

def onKit(kit: Kit): Unit

This comment has been minimized.

Copy link
@araspitzu

araspitzu Apr 2, 2019

Member

Not sure i would expose the entire Kit, in this way plugins enlarge the attack surface of eclair. An insecure plugin can be exploited to access channel data.

This comment has been minimized.

Copy link
@pm47

pm47 Apr 2, 2019

Author Member

It allows to do powerful stuff on top of eclair, and you are always taking risks by running someone else's code.


}

object Plugin {

def loadPlugins(jars: Seq[File]): Seq[Plugin] = {
val finder = ClassFinder(jars)
val classes = finder.getClasses
val urls = jars.map(f => new URL(s"file:${f.getCanonicalPath}"))
val loader = new URLClassLoader(urls.toArray, ClassLoader.getSystemClassLoader)
classes
.filter(_.isConcrete)
.filter(_.implements(classOf[Plugin].getName))
.map(c => Class.forName(c.name, true, loader).getDeclaredConstructor().newInstance().asInstanceOf[Plugin])
.toList
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.