Skip to content

Commit

Permalink
Electrum: add support for socks5 proxies (#1315)
Browse files Browse the repository at this point in the history
We use the socks5 proxy that is defined in the configuration and is typically used to connect to LN nodes running as TOR hidden services.
This should allow users to connect to Electrum servers that are running behind TOR.
  • Loading branch information
sstone authored Feb 11, 2020
1 parent bd05eb1 commit b730300
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 4 deletions.
2 changes: 1 addition & 1 deletion eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class Setup(datadir: File,
val stream = classOf[Setup].getResourceAsStream(addressesFile)
ElectrumClientPool.readServerAddresses(stream, sslEnabled)
}
val electrumClient = system.actorOf(SimpleSupervisor.props(Props(new ElectrumClientPool(blockCount, addresses)), "electrum-client", SupervisorStrategy.Resume))
val electrumClient = system.actorOf(SimpleSupervisor.props(Props(new ElectrumClientPool(blockCount, addresses, nodeParams.socksProxy_opt)), "electrum-client", SupervisorStrategy.Resume))
Electrum(electrumClient)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import akka.actor.{Actor, ActorLogging, ActorRef, Stash, Terminated}
import fr.acinq.bitcoin._
import fr.acinq.eclair.blockchain.bitcoind.rpc.{Error, JsonRPCRequest, JsonRPCResponse}
import fr.acinq.eclair.blockchain.electrum.ElectrumClient.SSL
import fr.acinq.eclair.tor.Socks5ProxyParams
import io.netty.bootstrap.Bootstrap
import io.netty.buffer.PooledByteBufAllocator
import io.netty.channel._
Expand All @@ -31,8 +32,10 @@ import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioSocketChannel
import io.netty.handler.codec.string.{LineEncoder, StringDecoder}
import io.netty.handler.codec.{LineBasedFrameDecoder, MessageToMessageDecoder, MessageToMessageEncoder}
import io.netty.handler.proxy.Socks5ProxyHandler
import io.netty.handler.ssl.SslContextBuilder
import io.netty.handler.ssl.util.InsecureTrustManagerFactory
import io.netty.resolver.{NoopAddressResolver, NoopAddressResolverGroup}
import io.netty.util.CharsetUtil
import org.json4s.JsonAST._
import org.json4s.jackson.JsonMethods
Expand All @@ -48,7 +51,7 @@ import scala.util.{Failure, Success, Try}
* For later optimizations, see http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html
*
*/
class ElectrumClient(serverAddress: InetSocketAddress, ssl: SSL)(implicit val ec: ExecutionContext) extends Actor with Stash with ActorLogging {
class ElectrumClient(serverAddress: InetSocketAddress, ssl: SSL, socksProxy_opt: Option[Socks5ProxyParams] = None)(implicit val ec: ExecutionContext) extends Actor with Stash with ActorLogging {

import ElectrumClient._

Expand Down Expand Up @@ -93,9 +96,14 @@ class ElectrumClient(serverAddress: InetSocketAddress, ssl: SSL)(implicit val ec
ch.pipeline.addLast(new JsonRPCRequestEncoder)
// error handler
ch.pipeline.addLast(new ExceptionHandler)
// optional proxy (must be the first handler)
socksProxy_opt.foreach(params => ch.pipeline().addFirst(new Socks5ProxyHandler(params.address)))
}
})

// don't try to resolve addresses if we're using a proxy
socksProxy_opt.foreach(params => b.resolver(NoopAddressResolverGroup.INSTANCE))

// Start the client.
log.info("connecting to server={}", serverAddress)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ import fr.acinq.bitcoin.BlockHeader
import fr.acinq.eclair.blockchain.CurrentBlockCount
import fr.acinq.eclair.blockchain.electrum.ElectrumClient.SSL
import fr.acinq.eclair.blockchain.electrum.ElectrumClientPool.ElectrumServerAddress
import fr.acinq.eclair.tor.Socks5ProxyParams
import org.json4s.JsonAST.{JObject, JString}
import org.json4s.jackson.JsonMethods

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import scala.util.Random

class ElectrumClientPool(blockCount: AtomicLong, serverAddresses: Set[ElectrumServerAddress])(implicit val ec: ExecutionContext) extends Actor with FSM[ElectrumClientPool.State, ElectrumClientPool.Data] {
class ElectrumClientPool(blockCount: AtomicLong, serverAddresses: Set[ElectrumServerAddress], socksProxy_opt: Option[Socks5ProxyParams] = None)(implicit val ec: ExecutionContext) extends Actor with FSM[ElectrumClientPool.State, ElectrumClientPool.Data] {
import ElectrumClientPool._

val statusListeners = collection.mutable.HashSet.empty[ActorRef]
Expand Down Expand Up @@ -112,7 +113,7 @@ class ElectrumClientPool(blockCount: AtomicLong, serverAddresses: Set[ElectrumSe
pickAddress(serverAddresses, addresses.values.toSet) match {
case Some(ElectrumServerAddress(address, ssl)) =>
val resolved = new InetSocketAddress(address.getHostName, address.getPort)
val client = context.actorOf(Props(new ElectrumClient(resolved, ssl)))
val client = context.actorOf(Props(new ElectrumClient(resolved, ssl, socksProxy_opt)))
client ! ElectrumClient.AddStatusListener(self)
// we watch each electrum client, they will stop on disconnection
context watch client
Expand Down

0 comments on commit b730300

Please sign in to comment.