Tor support for BTC RPC (#3470)

* Tor support for BTC RPC

* change config format
This commit is contained in:
rorp 2021-08-03 09:07:05 -07:00 committed by GitHub
parent 1051e6365a
commit ca40af5d94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 89 additions and 37 deletions

View File

@ -53,6 +53,8 @@ class BitcoindConfigPane(
minWidth = 300
}
private val torCheckBox: CheckBox = new CheckBox()
private var nextRow: Int = 0
val gridPane: GridPane = new GridPane() {
@ -80,6 +82,10 @@ class BitcoindConfigPane(
add(new Label("Bitcoin Core Version"), 0, nextRow)
add(versionComboBox, 1, nextRow)
nextRow += 1
add(new Label("Use Tor"), 0, nextRow)
add(torCheckBox, 1, nextRow)
nextRow += 1
}
val launchButton: Button = new Button("Launch Wallet") {
@ -94,7 +100,14 @@ class BitcoindConfigPane(
}
def getConfig: Config = {
val configStr =
val proxyConfStr =
if (hostTF.text.value.contains(".onion") || torCheckBox.selected.value) {
s"""
|bitcoin-s.proxy.enabled = true
|""".stripMargin
} else ""
val configStr = proxyConfStr +
s"""
|bitcoin-s.node.mode = bitcoind
|bitcoin-s.bitcoind-rpc.isRemote = true

View File

@ -70,6 +70,8 @@ class NeutrinoConfigPane(
minWidth = 300
}
private val torCheckBox: CheckBox = new CheckBox()
private var nextRow: Int = 0
val gridPane: GridPane = new GridPane() {
@ -84,6 +86,10 @@ class NeutrinoConfigPane(
add(new Label("Peer Address"), 0, nextRow)
add(peerAddressTF, 1, nextRow)
nextRow += 1
add(new Label("Use Tor"), 0, nextRow)
add(torCheckBox, 1, nextRow)
nextRow += 1
}
val launchButton: Button = new Button("Launch Wallet") {
@ -99,11 +105,15 @@ class NeutrinoConfigPane(
def getConfig: Config = {
// Auto-enable proxy for .onion peers
val proxyConfStr = if (peerAddressTF.text.value.contains(".onion")) {
s"""
|bitcoin-s.proxy.enabled = true
|""".stripMargin
} else ""
val proxyConfStr =
if (
peerAddressTF.text.value.contains(
".onion") || torCheckBox.selected.value
) {
s"""
|bitcoin-s.proxy.enabled = true
|""".stripMargin
} else ""
val configStr = proxyConfStr +
s"""
|bitcoin-s.network = ${DatadirUtil.networkStrToDirName(

View File

@ -2,11 +2,13 @@ package org.bitcoins.server
import akka.actor.ActorSystem
import com.typesafe.config.Config
import org.bitcoins.core.util.NetworkUtil
import org.bitcoins.db._
import org.bitcoins.node.NodeType
import org.bitcoins.node.config.NodeAppConfig
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
import org.bitcoins.rpc.config._
import org.bitcoins.tor.Socks5ProxyParams
import java.io.File
import java.net.{InetSocketAddress, URI}
@ -90,6 +92,22 @@ case class BitcoindRpcAppConfig(
lazy val rpcPassword: String =
config.getString("bitcoin-s.bitcoind-rpc.rpcpassword")
lazy val socks5ProxyParams: Option[Socks5ProxyParams] = {
if (config.getBoolean("bitcoin-s.proxy.enabled")) {
Some(
Socks5ProxyParams(
address = NetworkUtil.parseInetSocketAddress(
config.getString("bitcoin-s.proxy.socks5"),
Socks5ProxyParams.DefaultPort),
credentialsOpt = None,
randomizeCredentials = true
)
)
} else {
None
}
}
lazy val versionOpt: Option[BitcoindVersion] =
config
.getStringOrNone("bitcoin-s.bitcoind-rpc.version")
@ -140,7 +158,8 @@ case class BitcoindRpcAppConfig(
zmqConfig = zmqConfig,
binary = binaryOpt.getOrElse(fallbackBinary),
datadir = bitcoindDataDir,
isRemote = isRemote
isRemote = isRemote,
proxyParams = socks5ProxyParams
)
}

View File

@ -3,6 +3,10 @@ package org.bitcoins.rpc.client.common
import akka.actor.ActorSystem
import akka.http.javadsl.model.headers.HttpCredentials
import akka.http.scaladsl.model._
import akka.http.scaladsl.settings.{
ClientConnectionSettings,
ConnectionPoolSettings
}
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.http.scaladsl.{Http, HttpExt}
import akka.stream.StreamTcpException
@ -22,6 +26,7 @@ import org.bitcoins.rpc.config.BitcoindAuthCredentials.{
}
import org.bitcoins.rpc.config.{BitcoindAuthCredentials, BitcoindInstance}
import org.bitcoins.rpc.util.NativeProcessFactory
import org.bitcoins.tor.Socks5ClientTransport
import play.api.libs.json._
import java.nio.file.{Files, Path}
@ -325,8 +330,21 @@ trait Client
/** Cached http client to send requests to bitcoind with */
private lazy val httpClient: HttpExt = Http(system)
private lazy val httpConnectionPoolSettings: ConnectionPoolSettings =
instance.proxyParams match {
case Some(proxyParams) =>
val socks5ClientTransport = new Socks5ClientTransport(proxyParams)
val clientConnectionSettings =
ClientConnectionSettings(system).withTransport(socks5ClientTransport)
ConnectionPoolSettings(system).withConnectionSettings(
clientConnectionSettings)
case None => ConnectionPoolSettings(system)
}
protected def sendRequest(req: HttpRequest): Future[HttpResponse] = {
httpClient.singleRequest(req)
httpClient.singleRequest(req, settings = httpConnectionPoolSettings)
}
/** Parses the payload of the given response into JSON.

View File

@ -4,6 +4,7 @@ import grizzled.slf4j.Logging
import org.bitcoins.core.api.commons.InstanceFactory
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.rpc.client.common.BitcoindVersion
import org.bitcoins.tor.Socks5ProxyParams
import java.io.{File, FileNotFoundException}
import java.net.URI
@ -65,6 +66,8 @@ sealed trait BitcoindInstance extends Logging {
}
def p2pPort: Int = uri.getPort
def proxyParams: Option[Socks5ProxyParams]
}
object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
@ -77,7 +80,8 @@ object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
zmqConfig: ZmqConfig,
binary: File,
datadir: File,
isRemote: Boolean
isRemote: Boolean,
proxyParams: Option[Socks5ProxyParams]
) extends BitcoindInstance
def apply(
@ -88,7 +92,8 @@ object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
zmqConfig: ZmqConfig = ZmqConfig(),
binary: File = DEFAULT_BITCOIND_LOCATION,
datadir: File = BitcoindConfig.DEFAULT_DATADIR,
isRemote: Boolean = false
isRemote: Boolean = false,
proxyParams: Option[Socks5ProxyParams] = None
): BitcoindInstance = {
BitcoindInstanceImpl(network,
uri,
@ -97,7 +102,8 @@ object BitcoindInstance extends InstanceFactory[BitcoindInstance] {
zmqConfig = zmqConfig,
binary = binary,
datadir = datadir,
isRemote = isRemote)
isRemote = isRemote,
proxyParams = proxyParams)
}
lazy val DEFAULT_BITCOIND_LOCATION: File = {

View File

@ -122,7 +122,8 @@ lazy val bitcoindRpc = project
.settings(CommonSettings.prodSettings: _*)
.dependsOn(
asyncUtilsJVM,
appCommons
appCommons,
tor
)
lazy val eclairRpc = project

View File

@ -1,34 +1,18 @@
package org.bitcoins.core.util
import java.net.InetSocketAddress
import java.net.{InetSocketAddress, URI}
abstract class NetworkUtil {
private def parsePort(port: String): Int = {
lazy val errorMsg = s"Invalid peer port: $port"
try {
val res = port.toInt
if (res < 0 || res > 0xffff) {
throw new RuntimeException(errorMsg)
}
res
} catch {
case _: NumberFormatException =>
throw new RuntimeException(errorMsg)
}
}
/** Parses a string that looks like this to [[java.net.InetSocketAddress]]
* "neutrino.testnet3.suredbits.com:18333"
*/
def parseInetSocketAddress(
address: String,
defaultPort: Int): InetSocketAddress = {
address.split(":") match {
case Array(host) => new InetSocketAddress(host, defaultPort)
case Array(host, port) => new InetSocketAddress(host, parsePort(port))
case _ => throw new RuntimeException(s"Invalid peer address: $address")
}
val uri = new URI("tcp://" + address)
val port = if (uri.getPort < 0) defaultPort else uri.getPort
InetSocketAddress.createUnresolved(uri.getHost, port)
}
}

View File

@ -9,7 +9,7 @@ import org.bitcoins.chain.models.{
CompactFilterDAO,
CompactFilterHeaderDAO
}
import org.bitcoins.core.util.Mutable
import org.bitcoins.core.util.{Mutable, NetworkUtil}
import org.bitcoins.db.{AppConfigFactory, DbAppConfig, JdbcProfileComponent}
import org.bitcoins.node._
import org.bitcoins.node.db.NodeDbManagement
@ -17,7 +17,6 @@ import org.bitcoins.node.models.Peer
import org.bitcoins.node.networking.peer.DataMessageHandler
import org.bitcoins.tor.Socks5ProxyParams
import java.net.{InetSocketAddress, URI}
import java.nio.file.Path
import scala.concurrent.{ExecutionContext, Future}
@ -91,11 +90,11 @@ case class NodeAppConfig(
lazy val socks5ProxyParams: Option[Socks5ProxyParams] = {
if (config.getBoolean("bitcoin-s.proxy.enabled")) {
val uri = new URI("tcp://" + config.getString("bitcoin-s.proxy.socks5"))
val sock5 = InetSocketAddress.createUnresolved(uri.getHost, uri.getPort)
Some(
Socks5ProxyParams(
address = sock5,
address = NetworkUtil.parseInetSocketAddress(
config.getString("bitcoin-s.proxy.socks5"),
Socks5ProxyParams.DefaultPort),
credentialsOpt = None,
randomizeCredentials = true
)

View File

@ -251,6 +251,8 @@ case class Socks5ProxyParams(
object Socks5ProxyParams {
val DefaultPort = 9050
def proxyCredentials(
proxyParams: Socks5ProxyParams): Option[Socks5Connection.Credentials] =
if (proxyParams.randomizeCredentials) {