mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-18 21:34:39 +01:00
Eclair RPC 0.4.1 (#1627)
* Eclair RPC 0.4.1 * channelstats * remove the launch script editing code * getinfo * sendonchain, onchainbalance, onchaintransactions * cleanup * repond to the comments
This commit is contained in:
parent
194370622d
commit
43b6349758
@ -5,11 +5,12 @@ import java.time.Instant
|
||||
import java.util.UUID
|
||||
|
||||
import org.bitcoins.commons.serializers.JsonReaders._
|
||||
import org.bitcoins.core.config.BitcoinNetwork
|
||||
import org.bitcoins.core.currency.Satoshis
|
||||
import org.bitcoins.core.protocol.ln.channel.{ChannelState, FundedChannelId}
|
||||
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis
|
||||
import org.bitcoins.core.protocol.ln.fee.FeeProportionalMillionths
|
||||
import org.bitcoins.core.protocol.ln.node.NodeId
|
||||
import org.bitcoins.core.protocol.ln.node.{Feature, FeatureSupport, NodeId}
|
||||
import org.bitcoins.core.protocol.ln.{
|
||||
LnHumanReadablePart,
|
||||
PaymentPreimage,
|
||||
@ -28,11 +29,15 @@ import scala.concurrent.duration.FiniteDuration
|
||||
sealed abstract class EclairModels
|
||||
|
||||
case class GetInfoResult(
|
||||
version: String,
|
||||
nodeId: NodeId,
|
||||
alias: String,
|
||||
features: Features,
|
||||
chainHash: DoubleSha256Digest,
|
||||
network: BitcoinNetwork,
|
||||
blockHeight: Long,
|
||||
publicAddresses: Seq[InetSocketAddress])
|
||||
publicAddresses: Seq[InetSocketAddress],
|
||||
instanceId: UUID)
|
||||
|
||||
case class PeerInfo(
|
||||
nodeId: NodeId,
|
||||
@ -40,6 +45,31 @@ case class PeerInfo(
|
||||
address: Option[String],
|
||||
channels: Int)
|
||||
|
||||
case class ChannelCommandResult(
|
||||
results: scala.collection.Map[
|
||||
Either[ShortChannelId, FundedChannelId],
|
||||
ChannelCommandResult.State]
|
||||
)
|
||||
|
||||
object ChannelCommandResult {
|
||||
sealed trait State
|
||||
case object OK extends State
|
||||
case object ChannelOpened extends State
|
||||
case object ChannelClosed extends State
|
||||
case class Error(message: String) extends State
|
||||
|
||||
def fromString(s: String): State =
|
||||
if (s == "ok") {
|
||||
ChannelCommandResult.OK
|
||||
} else if (s.startsWith("created channel ")) {
|
||||
ChannelCommandResult.ChannelOpened
|
||||
} else if (s.startsWith("closed channel ")) {
|
||||
ChannelCommandResult.ChannelClosed
|
||||
} else {
|
||||
ChannelCommandResult.Error(s)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the data model returned by the RPC call
|
||||
* `channels nodeId`. The content of the objects
|
||||
@ -82,14 +112,23 @@ case class OpenChannelInfo(
|
||||
state: ChannelState.NORMAL.type
|
||||
) extends ChannelInfo
|
||||
|
||||
case class ActivatedFeature(feature: Feature, support: FeatureSupport)
|
||||
|
||||
case class UnknownFeature(bitIndex: Int)
|
||||
|
||||
case class Features(
|
||||
activated: Set[ActivatedFeature],
|
||||
unknown: Set[UnknownFeature])
|
||||
|
||||
case class NodeInfo(
|
||||
signature: ECDigitalSignature,
|
||||
features: String,
|
||||
features: Features,
|
||||
timestamp: Instant,
|
||||
nodeId: NodeId,
|
||||
rgbColor: String,
|
||||
alias: String,
|
||||
addresses: Vector[InetSocketAddress])
|
||||
addresses: Vector[InetSocketAddress],
|
||||
unknownFields: String)
|
||||
|
||||
case class ChannelDesc(shortChannelId: ShortChannelId, a: NodeId, b: NodeId)
|
||||
|
||||
@ -110,12 +149,31 @@ case class NetworkFeesResult(
|
||||
|
||||
case class ChannelStats(
|
||||
channelId: FundedChannelId,
|
||||
direction: ChannelStats.Direction,
|
||||
avgPaymentAmount: Satoshis,
|
||||
paymentCount: Long,
|
||||
relayFee: Satoshis,
|
||||
networkFee: Satoshis
|
||||
)
|
||||
|
||||
object ChannelStats {
|
||||
sealed trait Direction
|
||||
case object In extends Direction
|
||||
case object Out extends Direction
|
||||
|
||||
object Direction {
|
||||
|
||||
def fromString(s: String): Direction =
|
||||
if (s.toUpperCase == "IN") {
|
||||
ChannelStats.In
|
||||
} else if (s.toUpperCase == "OUT") {
|
||||
ChannelStats.Out
|
||||
} else {
|
||||
throw new RuntimeException(s"Unknown payment direction: `$s`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class UsableBalancesResult(
|
||||
remoteNodeId: NodeId,
|
||||
shortChannelId: ShortChannelId,
|
||||
@ -362,3 +420,14 @@ object WebSocketEvent {
|
||||
) extends WebSocketEvent
|
||||
|
||||
}
|
||||
|
||||
case class OnChainBalance(confirmed: Satoshis, unconfirmed: Satoshis)
|
||||
|
||||
case class WalletTransaction(
|
||||
address: String,
|
||||
amount: Satoshis,
|
||||
fees: Satoshis,
|
||||
blockHash: DoubleSha256DigestBE,
|
||||
confirmations: Long,
|
||||
txid: DoubleSha256DigestBE,
|
||||
timestamp: Long)
|
||||
|
@ -19,7 +19,7 @@ import org.bitcoins.core.protocol.ln._
|
||||
import org.bitcoins.core.protocol.ln.channel._
|
||||
import org.bitcoins.core.protocol.ln.currency._
|
||||
import org.bitcoins.core.protocol.ln.fee.FeeProportionalMillionths
|
||||
import org.bitcoins.core.protocol.ln.node.NodeId
|
||||
import org.bitcoins.core.protocol.ln.node.{Feature, FeatureSupport, NodeId}
|
||||
import org.bitcoins.core.protocol.script.{
|
||||
ScriptPubKey,
|
||||
ScriptSignature,
|
||||
@ -36,16 +36,7 @@ import org.bitcoins.core.protocol.{
|
||||
import org.bitcoins.core.script.ScriptType
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.core.wallet.fee.{BitcoinFeeUnit, SatoshisPerByte}
|
||||
import org.bitcoins.crypto.{
|
||||
DoubleSha256Digest,
|
||||
DoubleSha256DigestBE,
|
||||
ECDigitalSignature,
|
||||
ECPublicKey,
|
||||
RipeMd160Digest,
|
||||
RipeMd160DigestBE,
|
||||
Sha256Digest,
|
||||
Sha256Hash160Digest
|
||||
}
|
||||
import org.bitcoins.crypto._
|
||||
import play.api.libs.json._
|
||||
|
||||
import scala.concurrent.duration._
|
||||
@ -257,6 +248,13 @@ object JsonReaders {
|
||||
}
|
||||
}
|
||||
|
||||
implicit object BitcoinNetworkReads extends Reads[BitcoinNetwork] {
|
||||
|
||||
override def reads(json: JsValue): JsResult[BitcoinNetwork] =
|
||||
SerializerUtil.processJsString(BitcoinNetworks.fromString)(json)
|
||||
|
||||
}
|
||||
|
||||
// Errors for Unit return types are caught in RpcClient::checkUnit
|
||||
implicit object UnitReads extends Reads[Unit] {
|
||||
override def reads(json: JsValue): JsResult[Unit] = JsSuccess(())
|
||||
@ -718,6 +716,41 @@ object JsonReaders {
|
||||
}
|
||||
}
|
||||
|
||||
implicit val featureSupportReads: Reads[FeatureSupport] =
|
||||
Reads { jsValue =>
|
||||
SerializerUtil.processJsString {
|
||||
case "mandatory" => FeatureSupport.Mandatory
|
||||
case "optional" => FeatureSupport.Optional
|
||||
case err: String =>
|
||||
throw new RuntimeException(s"Invalid feature support value: `$err`")
|
||||
}(jsValue)
|
||||
}
|
||||
|
||||
lazy val featuresByName: Map[String, Feature] =
|
||||
Feature.knownFeatures.map(f => (f.rfcName, f)).toMap
|
||||
|
||||
implicit val featureReads: Reads[Feature] =
|
||||
Reads { jsValue =>
|
||||
SerializerUtil.processJsString(featuresByName)(jsValue)
|
||||
}
|
||||
|
||||
implicit val unknownFeatureReads: Reads[UnknownFeature] =
|
||||
Reads { jsValue =>
|
||||
SerializerUtil.processJsString(s => UnknownFeature(s.toInt))(jsValue)
|
||||
}
|
||||
|
||||
implicit val activatedFeatureReads: Reads[ActivatedFeature] =
|
||||
Reads { jsValue =>
|
||||
for {
|
||||
feature <- (jsValue \ "name").validate[Feature]
|
||||
support <- (jsValue \ "support").validate[FeatureSupport]
|
||||
} yield ActivatedFeature(feature, support)
|
||||
}
|
||||
|
||||
implicit val featuresReads: Reads[Features] = {
|
||||
Json.reads[Features]
|
||||
}
|
||||
|
||||
implicit val getInfoResultReads: Reads[GetInfoResult] = {
|
||||
Json.reads[GetInfoResult]
|
||||
}
|
||||
@ -737,20 +770,22 @@ object JsonReaders {
|
||||
Reads { jsValue =>
|
||||
for {
|
||||
signature <- (jsValue \ "signature").validate[ECDigitalSignature]
|
||||
features <- (jsValue \ "features").validate[String]
|
||||
features <- (jsValue \ "features").validate[Features]
|
||||
timestamp <- (jsValue \ "timestamp")
|
||||
.validate[Instant](instantReadsSeconds)
|
||||
nodeId <- (jsValue \ "nodeId").validate[NodeId]
|
||||
rgbColor <- (jsValue \ "rgbColor").validate[String]
|
||||
alias <- (jsValue \ "alias").validate[String]
|
||||
addresses <- (jsValue \ "addresses").validate[Vector[InetSocketAddress]]
|
||||
unknownFields <- (jsValue \ "unknownFields").validate[String]
|
||||
} yield NodeInfo(signature,
|
||||
features,
|
||||
timestamp,
|
||||
nodeId,
|
||||
rgbColor,
|
||||
alias,
|
||||
addresses)
|
||||
addresses,
|
||||
unknownFields)
|
||||
}
|
||||
}
|
||||
|
||||
@ -843,6 +878,25 @@ object JsonReaders {
|
||||
}
|
||||
}
|
||||
|
||||
implicit val channelCommandResultStateReads: Reads[
|
||||
ChannelCommandResult.State] = Reads { jsValue =>
|
||||
SerializerUtil.processJsString(ChannelCommandResult.fromString)(jsValue)
|
||||
}
|
||||
|
||||
implicit val channelCommandResultReads: Reads[ChannelCommandResult] = Reads {
|
||||
case obj: JsObject =>
|
||||
JsSuccess(ChannelCommandResult(obj.value.map { x =>
|
||||
val channelId = Try(FundedChannelId.fromHex(x._1)) match {
|
||||
case Success(id) => Right(id)
|
||||
case Failure(_) => Left(ShortChannelId.fromHumanReadableString(x._1))
|
||||
}
|
||||
(channelId, x._2.validate[ChannelCommandResult.State].get)
|
||||
}))
|
||||
case err @ (JsNull | _: JsBoolean | _: JsString | _: JsArray |
|
||||
_: JsNumber) =>
|
||||
SerializerUtil.buildJsErrorMsg("jsobject", err)
|
||||
}
|
||||
|
||||
implicit val channelUpdateReads: Reads[ChannelUpdate] = {
|
||||
Reads { jsValue =>
|
||||
for {
|
||||
@ -1129,6 +1183,11 @@ object JsonReaders {
|
||||
timestamp)
|
||||
}
|
||||
|
||||
implicit val channelStatsDirectionReads: Reads[ChannelStats.Direction] =
|
||||
Reads { json =>
|
||||
SerializerUtil.processJsString(ChannelStats.Direction.fromString)(json)
|
||||
}
|
||||
|
||||
implicit val channelStatsReads: Reads[ChannelStats] =
|
||||
Json.reads[ChannelStats]
|
||||
|
||||
@ -1243,4 +1302,10 @@ object JsonReaders {
|
||||
}
|
||||
}
|
||||
|
||||
implicit val onChainBalanceReads: Reads[OnChainBalance] =
|
||||
Json.reads[OnChainBalance]
|
||||
|
||||
implicit val walletTransactionReads: Reads[WalletTransaction] =
|
||||
Json.reads[WalletTransaction]
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
package org.bitcoins.core.protocol.ln.node
|
||||
|
||||
sealed trait FeatureSupport
|
||||
|
||||
object FeatureSupport {
|
||||
|
||||
case object Mandatory extends FeatureSupport {
|
||||
override def toString: String = "mandatory"
|
||||
}
|
||||
|
||||
case object Optional extends FeatureSupport {
|
||||
override def toString: String = "optional"
|
||||
}
|
||||
}
|
||||
|
||||
sealed trait Feature {
|
||||
def rfcName: String
|
||||
def mandatory: Int
|
||||
def optional: Int = mandatory + 1
|
||||
|
||||
def supportBit(support: FeatureSupport): Int =
|
||||
support match {
|
||||
case FeatureSupport.Mandatory => mandatory
|
||||
case FeatureSupport.Optional => optional
|
||||
}
|
||||
|
||||
override def toString = rfcName
|
||||
}
|
||||
|
||||
object Feature {
|
||||
|
||||
case object OptionDataLossProtect extends Feature {
|
||||
val rfcName = "option_data_loss_protect"
|
||||
val mandatory = 0
|
||||
}
|
||||
|
||||
case object InitialRoutingSync extends Feature {
|
||||
val rfcName = "initial_routing_sync"
|
||||
// reserved but not used as per lightningnetwork/lightning-rfc/pull/178
|
||||
val mandatory = 2
|
||||
}
|
||||
|
||||
case object ChannelRangeQueries extends Feature {
|
||||
val rfcName = "gossip_queries"
|
||||
val mandatory = 6
|
||||
}
|
||||
|
||||
case object VariableLengthOnion extends Feature {
|
||||
val rfcName = "var_onion_optin"
|
||||
val mandatory = 8
|
||||
}
|
||||
|
||||
case object ChannelRangeQueriesExtended extends Feature {
|
||||
val rfcName = "gossip_queries_ex"
|
||||
val mandatory = 10
|
||||
}
|
||||
|
||||
case object StaticRemoteKey extends Feature {
|
||||
val rfcName = "option_static_remotekey"
|
||||
val mandatory = 12
|
||||
}
|
||||
|
||||
case object PaymentSecret extends Feature {
|
||||
val rfcName = "payment_secret"
|
||||
val mandatory = 14
|
||||
}
|
||||
|
||||
case object BasicMultiPartPayment extends Feature {
|
||||
val rfcName = "basic_mpp"
|
||||
val mandatory = 16
|
||||
}
|
||||
|
||||
case object Wumbo extends Feature {
|
||||
val rfcName = "option_support_large_channel"
|
||||
val mandatory = 18
|
||||
}
|
||||
|
||||
case object TrampolinePayment extends Feature {
|
||||
val rfcName = "trampoline_payment"
|
||||
val mandatory = 50
|
||||
}
|
||||
|
||||
val knownFeatures: Set[Feature] = Set(
|
||||
OptionDataLossProtect,
|
||||
InitialRoutingSync,
|
||||
ChannelRangeQueries,
|
||||
VariableLengthOnion,
|
||||
ChannelRangeQueriesExtended,
|
||||
PaymentSecret,
|
||||
BasicMultiPartPayment,
|
||||
Wumbo,
|
||||
TrampolinePayment,
|
||||
StaticRemoteKey
|
||||
)
|
||||
}
|
@ -3,16 +3,9 @@ package org.bitcoins.eclair.rpc
|
||||
import java.nio.file.Files
|
||||
import java.time.Instant
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.eclair.{
|
||||
ChannelResult,
|
||||
ChannelUpdate,
|
||||
IncomingPaymentStatus,
|
||||
InvoiceResult,
|
||||
OpenChannelInfo,
|
||||
OutgoingPaymentStatus,
|
||||
WebSocketEvent
|
||||
}
|
||||
import org.bitcoins.core.currency.{CurrencyUnit, CurrencyUnits, Satoshis}
|
||||
import org.bitcoins.commons.jsonmodels.eclair._
|
||||
import org.bitcoins.core.config.RegTest
|
||||
import org.bitcoins.core.currency.{CurrencyUnits, Satoshis}
|
||||
import org.bitcoins.core.number.UInt64
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.ln.LnParams.LnBitcoinRegTest
|
||||
@ -149,6 +142,23 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
|
||||
executeWithClientOtherClient(f)
|
||||
}
|
||||
|
||||
it should "perform on-chain operations" in {
|
||||
for {
|
||||
c <- clientF
|
||||
address <- c.getNewAddress()
|
||||
balance <- c.onChainBalance()
|
||||
txid <- c.sendOnChain(address, Satoshis(5000), 1)
|
||||
balance1 <- c.onChainBalance()
|
||||
transactions <- c.onChainTransactions()
|
||||
} yield {
|
||||
assert(balance.confirmed > Satoshis(0))
|
||||
assert(balance.unconfirmed == Satoshis(0))
|
||||
// we sent 5000 sats to ourselves and paid some sats in fee
|
||||
assert(balance1.confirmed < balance.confirmed)
|
||||
assert(transactions.exists(_.txid == txid))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Please keep this test the very first. All other tests rely on the propagated gossip messages.
|
||||
*/
|
||||
@ -1189,15 +1199,6 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
|
||||
}
|
||||
}
|
||||
|
||||
it should "get new address" in {
|
||||
for {
|
||||
c <- clientF
|
||||
res <- c.getNewAddress()
|
||||
} yield {
|
||||
assert(res.toString.nonEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
it should "disconnect node" in {
|
||||
for {
|
||||
c1 <- clientF
|
||||
@ -1205,6 +1206,8 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
|
||||
nodeInfo2 <- c2.getInfo
|
||||
_ <- c1.disconnect(nodeInfo2.nodeId)
|
||||
} yield {
|
||||
assert(nodeInfo2.features.activated.nonEmpty)
|
||||
assert(nodeInfo2.network == RegTest)
|
||||
succeed
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ TaskKeys.downloadEclair := {
|
||||
Files.createDirectories(binaryDir)
|
||||
}
|
||||
|
||||
val version = "0.4"
|
||||
val commit = "69c538e"
|
||||
val version = "0.4.1"
|
||||
val commit = "e5fb281"
|
||||
|
||||
logger.debug(s"(Maybe) downloading Eclair binaries for version: $version")
|
||||
|
||||
@ -49,36 +49,6 @@ TaskKeys.downloadEclair := {
|
||||
logger.info(s"Deleting archive")
|
||||
Files.delete(archiveLocation)
|
||||
|
||||
fixShebang(
|
||||
versionDir resolve s"eclair-node-$version-$commit" resolve "bin" resolve "eclair-node.sh")
|
||||
|
||||
logger.info(s"Download complete")
|
||||
}
|
||||
|
||||
// remove me when https://github.com/ACINQ/eclair/issues/1421
|
||||
// and https://github.com/ACINQ/eclair/issues/1422 are fixed
|
||||
def fixShebang(scriptPath: Path): Unit = {
|
||||
import java.nio.file.attribute.PosixFilePermissions
|
||||
import scala.io.Source
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
val tempPath = scriptPath.getParent resolve scriptPath.getFileName.toString + ".tmp"
|
||||
Files.createFile(tempPath,
|
||||
PosixFilePermissions.asFileAttribute(
|
||||
PosixFilePermissions.fromString("rwxr-xr-x")))
|
||||
val source = Source
|
||||
.fromFile(scriptPath.toUri)
|
||||
|
||||
val lines = (Vector("#!/usr/bin/env bash") ++ source.getLines()).map(
|
||||
line =>
|
||||
if (line == "declare -r lib_dir=\"$(realpath \"${app_home::-4}/lib\")\" # {app_home::-4} transforms ../bin in ../")
|
||||
"declare -r lib_dir=\"$(realpath \"${app_home:0:${#app_home}-4}/lib\")\" # {app_home:0:${#app_home}-4} transforms ../bin in ../"
|
||||
else line)
|
||||
|
||||
source.close()
|
||||
|
||||
Files.write(tempPath, lines.asJava, StandardOpenOption.WRITE)
|
||||
|
||||
tempPath.toFile.renameTo(scriptPath.toFile)
|
||||
}
|
||||
}
|
||||
|
@ -3,25 +3,7 @@ package org.bitcoins.eclair.rpc.api
|
||||
import java.net.InetSocketAddress
|
||||
import java.time.Instant
|
||||
|
||||
import org.bitcoins.commons.jsonmodels.eclair.{
|
||||
AuditResult,
|
||||
ChannelDesc,
|
||||
ChannelInfo,
|
||||
ChannelResult,
|
||||
ChannelStats,
|
||||
ChannelUpdate,
|
||||
GetInfoResult,
|
||||
IncomingPayment,
|
||||
InvoiceResult,
|
||||
NetworkFeesResult,
|
||||
NodeInfo,
|
||||
OutgoingPayment,
|
||||
PaymentId,
|
||||
PeerInfo,
|
||||
SendToRouteResult,
|
||||
UsableBalancesResult,
|
||||
WebSocketEvent
|
||||
}
|
||||
import org.bitcoins.commons.jsonmodels.eclair._
|
||||
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
|
||||
import org.bitcoins.core.protocol.ln.channel.{ChannelId, FundedChannelId}
|
||||
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis
|
||||
@ -35,7 +17,7 @@ import org.bitcoins.core.protocol.ln.{
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.core.protocol.{Address, BitcoinAddress}
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerByte
|
||||
import org.bitcoins.crypto.Sha256Digest
|
||||
import org.bitcoins.crypto.{DoubleSha256DigestBE, Sha256Digest}
|
||||
import org.bitcoins.eclair.rpc.network.NodeUri
|
||||
|
||||
import scala.concurrent.duration._
|
||||
@ -93,7 +75,7 @@ trait EclairApi {
|
||||
|
||||
def disconnect(nodeId: NodeId): Future[Unit]
|
||||
|
||||
def close(id: ChannelId, spk: ScriptPubKey): Future[Unit]
|
||||
def close(id: ChannelId, spk: ScriptPubKey): Future[ChannelCommandResult]
|
||||
|
||||
def findRoute(
|
||||
nodeId: NodeId,
|
||||
@ -105,9 +87,9 @@ trait EclairApi {
|
||||
invoice: LnInvoice,
|
||||
amountMsat: MilliSatoshis): Future[Vector[NodeId]]
|
||||
|
||||
def forceClose(channelId: ChannelId): Future[Unit]
|
||||
def forceClose(channelId: ChannelId): Future[ChannelCommandResult]
|
||||
|
||||
def forceClose(shortChannelId: ShortChannelId): Future[Unit]
|
||||
def forceClose(shortChannelId: ShortChannelId): Future[ChannelCommandResult]
|
||||
|
||||
def getInfo: Future[GetInfoResult]
|
||||
|
||||
@ -120,13 +102,13 @@ trait EclairApi {
|
||||
def updateRelayFee(
|
||||
channelId: ChannelId,
|
||||
feeBaseMsat: MilliSatoshis,
|
||||
feePropertionalMillionths: Long): Future[Unit]
|
||||
feePropertionalMillionths: Long): Future[ChannelCommandResult]
|
||||
|
||||
def updateRelayFee(
|
||||
shortChannelId: ShortChannelId,
|
||||
feeBaseMsat: MilliSatoshis,
|
||||
feePropertionalMillionths: Long
|
||||
): Future[Unit]
|
||||
): Future[ChannelCommandResult]
|
||||
|
||||
def open(
|
||||
nodeId: NodeId,
|
||||
@ -296,4 +278,13 @@ trait EclairApi {
|
||||
def connectToWebSocket(eventHandler: WebSocketEvent => Unit): Future[Unit]
|
||||
|
||||
def getNewAddress(): Future[BitcoinAddress]
|
||||
|
||||
def onChainBalance(): Future[OnChainBalance]
|
||||
|
||||
def onChainTransactions(): Future[Vector[WalletTransaction]]
|
||||
|
||||
def sendOnChain(
|
||||
address: BitcoinAddress,
|
||||
amount: Satoshis,
|
||||
confirmationTarget: Int): Future[DoubleSha256DigestBE]
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.core.protocol.{Address, BitcoinAddress}
|
||||
import org.bitcoins.core.util.{BytesUtil, FutureUtil, StartStop}
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerByte
|
||||
import org.bitcoins.crypto.Sha256Digest
|
||||
import org.bitcoins.crypto.{DoubleSha256DigestBE, Sha256Digest}
|
||||
import org.bitcoins.eclair.rpc.api._
|
||||
import org.bitcoins.eclair.rpc.config.EclairInstance
|
||||
import org.bitcoins.eclair.rpc.network.NodeUri
|
||||
@ -65,7 +65,7 @@ class EclairRpcClient(
|
||||
}
|
||||
|
||||
override def allNodes(): Future[Vector[NodeInfo]] = {
|
||||
eclairCall[Vector[NodeInfo]]("allnodes")
|
||||
eclairCall[Vector[NodeInfo]]("nodes")
|
||||
}
|
||||
|
||||
override def allUpdates(): Future[Vector[ChannelUpdate]] =
|
||||
@ -108,22 +108,22 @@ class EclairRpcClient(
|
||||
private def close(
|
||||
channelId: ChannelId,
|
||||
shortChannelId: Option[ShortChannelId],
|
||||
scriptPubKey: Option[ScriptPubKey]): Future[Unit] = {
|
||||
scriptPubKey: Option[ScriptPubKey]): Future[ChannelCommandResult] = {
|
||||
val params =
|
||||
Seq("channelId" -> channelId.hex) ++ Seq(
|
||||
shortChannelId.map(x => "shortChannelId" -> x.toString),
|
||||
scriptPubKey.map(x => "scriptPubKey" -> BytesUtil.encodeHex(x.asmBytes))
|
||||
).flatten
|
||||
|
||||
eclairCall[String]("close", params: _*).map(_ => ())
|
||||
eclairCall[ChannelCommandResult]("close", params: _*)
|
||||
}
|
||||
|
||||
def close(channelId: ChannelId): Future[Unit] =
|
||||
def close(channelId: ChannelId): Future[ChannelCommandResult] =
|
||||
close(channelId, scriptPubKey = None, shortChannelId = None)
|
||||
|
||||
override def close(
|
||||
channelId: ChannelId,
|
||||
scriptPubKey: ScriptPubKey): Future[Unit] = {
|
||||
scriptPubKey: ScriptPubKey): Future[ChannelCommandResult] = {
|
||||
close(channelId, scriptPubKey = Some(scriptPubKey), shortChannelId = None)
|
||||
}
|
||||
|
||||
@ -173,13 +173,16 @@ class EclairRpcClient(
|
||||
eclairCall[Vector[NodeId]]("findroute", params: _*)
|
||||
}
|
||||
|
||||
override def forceClose(channelId: ChannelId): Future[Unit] = {
|
||||
eclairCall[String]("forceclose", "channelId" -> channelId.hex).map(_ => ())
|
||||
override def forceClose(
|
||||
channelId: ChannelId): Future[ChannelCommandResult] = {
|
||||
eclairCall[ChannelCommandResult]("forceclose", "channelId" -> channelId.hex)
|
||||
}
|
||||
|
||||
override def forceClose(shortChannelId: ShortChannelId): Future[Unit] = {
|
||||
eclairCall[String]("forceclose",
|
||||
"shortChannelId" -> shortChannelId.toString).map(_ => ())
|
||||
override def forceClose(
|
||||
shortChannelId: ShortChannelId): Future[ChannelCommandResult] = {
|
||||
eclairCall[ChannelCommandResult](
|
||||
"forceclose",
|
||||
"shortChannelId" -> shortChannelId.toString)
|
||||
}
|
||||
|
||||
override def getInfo: Future[GetInfoResult] = {
|
||||
@ -530,8 +533,8 @@ class EclairRpcClient(
|
||||
override def updateRelayFee(
|
||||
channelId: ChannelId,
|
||||
feeBaseMsat: MilliSatoshis,
|
||||
feeProportionalMillionths: Long): Future[Unit] = {
|
||||
eclairCall[Unit](
|
||||
feeProportionalMillionths: Long): Future[ChannelCommandResult] = {
|
||||
eclairCall[ChannelCommandResult](
|
||||
"updaterelayfee",
|
||||
"channelId" -> channelId.hex,
|
||||
"feeBaseMsat" -> feeBaseMsat.toLong.toString,
|
||||
@ -542,8 +545,8 @@ class EclairRpcClient(
|
||||
override def updateRelayFee(
|
||||
shortChannelId: ShortChannelId,
|
||||
feeBaseMsat: MilliSatoshis,
|
||||
feeProportionalMillionths: Long): Future[Unit] = {
|
||||
eclairCall[Unit](
|
||||
feeProportionalMillionths: Long): Future[ChannelCommandResult] = {
|
||||
eclairCall[ChannelCommandResult](
|
||||
"updaterelayfee",
|
||||
"shortChannelId" -> shortChannelId.toHumanReadableString,
|
||||
"feeBaseMsat" -> feeBaseMsat.toLong.toString,
|
||||
@ -609,6 +612,25 @@ class EclairRpcClient(
|
||||
eclairCall[BitcoinAddress]("getnewaddress")
|
||||
}
|
||||
|
||||
override def onChainBalance(): Future[OnChainBalance] = {
|
||||
eclairCall[OnChainBalance]("onchainbalance")
|
||||
}
|
||||
|
||||
override def onChainTransactions(): Future[Vector[WalletTransaction]] = {
|
||||
eclairCall[Vector[WalletTransaction]]("onchaintransactions")
|
||||
}
|
||||
|
||||
override def sendOnChain(
|
||||
address: BitcoinAddress,
|
||||
amount: Satoshis,
|
||||
confirmationTarget: Int): Future[DoubleSha256DigestBE] = {
|
||||
eclairCall[DoubleSha256DigestBE](
|
||||
"sendonchain",
|
||||
"address" -> address.toString,
|
||||
"amountSatoshis" -> amount.toLong.toString,
|
||||
"confirmationTarget" -> confirmationTarget.toString)
|
||||
}
|
||||
|
||||
private def eclairCall[T](command: String, parameters: (String, String)*)(
|
||||
implicit reader: Reads[T]): Future[T] = {
|
||||
val request = buildRequest(getDaemon, command, parameters: _*)
|
||||
@ -946,8 +968,8 @@ object EclairRpcClient {
|
||||
implicit system: ActorSystem) = new EclairRpcClient(instance, binary)
|
||||
|
||||
/** The current commit we support of Eclair */
|
||||
private[bitcoins] val commit = "69c538e"
|
||||
private[bitcoins] val commit = "e5fb281"
|
||||
|
||||
/** The current version we support of Eclair */
|
||||
private[bitcoins] val version = "0.4"
|
||||
private[bitcoins] val version = "0.4.1"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user