Update Eclair RPC to v0.8.0 (#4994)

* Update Eclair RPC to v0.8.0

* update unit tests
This commit is contained in:
rorp 2023-03-04 07:02:36 -08:00 committed by GitHub
parent fbcea17e2f
commit 2799d8276a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 160 additions and 44 deletions

View file

@ -184,12 +184,20 @@ object ChannelStats {
}
}
case class RealChannelId(status: String, realScid: ShortChannelId)
case class ShortIds(
real: RealChannelId,
localAlias: String,
remoteAlias: String)
case class UsableBalancesResult(
remoteNodeId: NodeId,
shortChannelId: ShortChannelId,
shortIds: ShortIds,
canSend: MilliSatoshis,
canReceive: MilliSatoshis,
isPublic: Boolean
isPublic: Boolean,
isEnabled: Boolean
)
case class ReceivedPayment(
@ -258,7 +266,7 @@ case class ChannelResult(
data: JsObject) {
lazy val shortChannelId: Option[ShortChannelId] =
(data \ "shortChannelId").validate[ShortChannelId].asOpt
(data \ "shortIds" \ "real" \ "realScid").validate[ShortChannelId].asOpt
}
// ChannelResult ends here

View file

@ -952,6 +952,12 @@ object JsonReaders {
}
}
implicit val realChannelIdReads: Reads[RealChannelId] =
Json.reads[RealChannelId]
implicit val shortIdsReads: Reads[ShortIds] =
Json.reads[ShortIds]
implicit val nodeInfoReads: Reads[NodeInfo] = {
Reads { jsValue =>
for {
@ -1019,7 +1025,7 @@ object JsonReaders {
implicit val openChannelInfoReads: Reads[OpenChannelInfo] = Reads { jsValue =>
for {
nodeId <- (jsValue \ "nodeId").validate[NodeId]
shortChannelId <- (jsValue \ "data" \ "shortChannelId")
shortChannelId <- (jsValue \ "data" \ "shortIds" \ "real" \ "realScid")
.validate[ShortChannelId]
channelId <- (jsValue \ "channelId").validate[FundedChannelId]
state <- (jsValue \ "state").validate[ChannelState.NORMAL.type]
@ -1310,7 +1316,7 @@ object JsonReaders {
implicit val receivedPaymentResultReads: Reads[IncomingPayment] = Reads {
js =>
for {
paymentRequest <- (js \ "paymentRequest").validate[PaymentRequest]
paymentRequest <- (js \ "invoice").validate[PaymentRequest]
paymentPreimage <- (js \ "paymentPreimage").validate[PaymentPreimage]
paymentType <- (js \ "paymentType").validate[PaymentType]
createdAt <- (js \ "createdAt" \ "unix")

View file

@ -8,13 +8,20 @@ sealed trait ChannelState
object ChannelState extends StringFactory[ChannelState] {
case object WAIT_FOR_INIT_INTERNAL extends ChannelState
case object WAIT_FOR_INIT_SINGLE_FUNDED_CHANNEL extends ChannelState
case object WAIT_FOR_OPEN_CHANNEL extends ChannelState
case object WAIT_FOR_ACCEPT_CHANNEL extends ChannelState
case object WAIT_FOR_FUNDING_INTERNAL extends ChannelState
case object WAIT_FOR_FUNDING_CREATED extends ChannelState
case object WAIT_FOR_FUNDING_SIGNED extends ChannelState
case object WAIT_FOR_FUNDING_CONFIRMED extends ChannelState
case object WAIT_FOR_FUNDING_LOCKED extends ChannelState
case object WAIT_FOR_CHANNEL_READY extends ChannelState
case object WAIT_FOR_INIT_DUAL_FUNDED_CHANNEL extends ChannelState
case object WAIT_FOR_OPEN_DUAL_FUNDED_CHANNEL extends ChannelState
case object WAIT_FOR_ACCEPT_DUAL_FUNDED_CHANNEL extends ChannelState
case object WAIT_FOR_DUAL_FUNDING_CREATED extends ChannelState
case object WAIT_FOR_DUAL_FUNDING_CONFIRMED extends ChannelState
case object WAIT_FOR_DUAL_FUNDING_READY extends ChannelState
case object NORMAL extends ChannelState
case object SHUTDOWN extends ChannelState
case object NEGOTIATING extends ChannelState
@ -23,19 +30,24 @@ object ChannelState extends StringFactory[ChannelState] {
case object OFFLINE extends ChannelState
case object SYNCING extends ChannelState
case object WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT extends ChannelState
case object ERR_FUNDING_LOST extends ChannelState
case object ERR_FUNDING_TIMEOUT extends ChannelState
case object ERR_INFORMATION_LEAK extends ChannelState
private lazy val all: Map[String, ChannelState] = List(
WAIT_FOR_INIT_INTERNAL,
WAIT_FOR_INIT_SINGLE_FUNDED_CHANNEL,
WAIT_FOR_OPEN_CHANNEL,
WAIT_FOR_ACCEPT_CHANNEL,
WAIT_FOR_FUNDING_INTERNAL,
WAIT_FOR_FUNDING_CREATED,
WAIT_FOR_FUNDING_SIGNED,
WAIT_FOR_FUNDING_CONFIRMED,
WAIT_FOR_FUNDING_LOCKED,
WAIT_FOR_CHANNEL_READY,
WAIT_FOR_INIT_DUAL_FUNDED_CHANNEL,
WAIT_FOR_OPEN_DUAL_FUNDED_CHANNEL,
WAIT_FOR_ACCEPT_DUAL_FUNDED_CHANNEL,
WAIT_FOR_DUAL_FUNDING_CREATED,
WAIT_FOR_DUAL_FUNDING_CONFIRMED,
WAIT_FOR_DUAL_FUNDING_READY,
NORMAL,
SHUTDOWN,
NEGOTIATING,
@ -44,8 +56,6 @@ object ChannelState extends StringFactory[ChannelState] {
OFFLINE,
SYNCING,
WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT,
ERR_FUNDING_LOST,
ERR_FUNDING_TIMEOUT,
ERR_INFORMATION_LEAK
).map(state => state.toString -> state).toMap

View file

@ -38,7 +38,19 @@ trait NodeFeature extends Feature
/** Feature that should be advertised in invoices. */
trait InvoiceFeature extends Feature
// @formatter:on
/** Feature negotiated when opening a channel that will apply for all of the channel's lifetime.
* This doesn't include features that can be safely activated/deactivated without impacting the channel's operation such
* as option_dataloss_protect or option_shutdown_anysegwit.
*/
trait PermanentChannelFeature extends InitFeature // <- not in the spec
/** Permanent channel feature negotiated in the channel type. Those features take precedence over permanent channel
* features negotiated in init messages. For example, if the channel type is option_static_remotekey, then even if
* the option_anchor_outputs feature is supported by both peers, it won't apply to the channel.
*/
trait ChannelTypeFeature extends PermanentChannelFeature // @formatter:on
case class UnknownFeature(bitIndex: Int)
@ -153,7 +165,8 @@ object Features {
case object UpfrontShutdownScript
extends Feature
with InitFeature
with NodeFeature {
with NodeFeature
with PermanentChannelFeature {
val rfcName = "option_upfront_shutdown_script"
val mandatory = 4
}
@ -186,7 +199,8 @@ object Features {
case object StaticRemoteKey
extends Feature
with InitFeature
with NodeFeature {
with NodeFeature
with ChannelTypeFeature {
val rfcName = "option_static_remotekey"
val mandatory = 12
}
@ -209,12 +223,20 @@ object Features {
val mandatory = 16
}
case object Wumbo extends Feature with InitFeature with NodeFeature {
case object Wumbo
extends Feature
with InitFeature
with NodeFeature
with PermanentChannelFeature {
val rfcName = "option_support_large_channel"
val mandatory = 18
}
case object AnchorOutputs extends Feature with InitFeature with NodeFeature {
case object AnchorOutputs
extends Feature
with InitFeature
with NodeFeature
with ChannelTypeFeature {
val rfcName = "option_anchor_outputs"
val mandatory = 20
}
@ -222,11 +244,21 @@ object Features {
case object AnchorOutputsZeroFeeHtlcTx
extends Feature
with InitFeature
with NodeFeature {
with NodeFeature
with ChannelTypeFeature {
val rfcName = "option_anchors_zero_fee_htlc_tx"
val mandatory = 22
}
case object RouteBlinding
extends Feature
with InitFeature
with NodeFeature
with InvoiceFeature {
val rfcName = "option_route_blinding"
val mandatory = 24
}
case object ShutdownAnySegwit
extends Feature
with InitFeature
@ -235,7 +267,11 @@ object Features {
val mandatory = 26
}
case object DualFunding extends Feature with InitFeature with NodeFeature {
case object DualFunding
extends Feature
with InitFeature
with NodeFeature
with PermanentChannelFeature {
val rfcName = "option_dual_fund"
val mandatory = 28
}
@ -250,11 +286,29 @@ object Features {
val mandatory = 44
}
case object ScidAlias
extends Feature
with InitFeature
with NodeFeature
with ChannelTypeFeature {
val rfcName = "option_scid_alias"
val mandatory = 46
}
case object PaymentMetadata extends Feature with InvoiceFeature {
val rfcName = "option_payment_metadata"
val mandatory = 48
}
case object ZeroConf
extends Feature
with InitFeature
with NodeFeature
with ChannelTypeFeature {
val rfcName = "option_zeroconf"
val mandatory = 50
}
case object KeySend extends Feature with NodeFeature {
val rfcName = "keysend"
val mandatory = 54
@ -275,6 +329,15 @@ object Features {
val mandatory = 148
}
// TODO: @remyers update feature bits once spec-ed (currently reserved here: https://github.com/lightning/bolts/pull/989)
case object AsyncPaymentPrototype
extends Feature
with InitFeature
with InvoiceFeature {
val rfcName = "async_payment_prototype"
val mandatory = 152
}
val knownFeatures: Set[Feature] = Set(
DataLossProtect,
InitialRoutingSync,
@ -288,13 +351,17 @@ object Features {
StaticRemoteKey,
AnchorOutputs,
AnchorOutputsZeroFeeHtlcTx,
RouteBlinding,
ShutdownAnySegwit,
DualFunding,
OnionMessages,
ChannelType,
ScidAlias,
PaymentMetadata,
ZeroConf,
KeySend,
TrampolinePaymentPrototype,
KeySend
AsyncPaymentPrototype
)
// Features may depend on other features, as specified in Bolt 9.
@ -304,9 +371,10 @@ object Features {
BasicMultiPartPayment -> (PaymentSecret :: Nil),
AnchorOutputs -> (StaticRemoteKey :: Nil),
AnchorOutputsZeroFeeHtlcTx -> (StaticRemoteKey :: Nil),
DualFunding -> (AnchorOutputsZeroFeeHtlcTx :: Nil),
RouteBlinding -> (VariableLengthOnion :: Nil),
TrampolinePaymentPrototype -> (PaymentSecret :: Nil),
KeySend -> (VariableLengthOnion :: Nil)
KeySend -> (VariableLengthOnion :: Nil),
AsyncPaymentPrototype -> (TrampolinePaymentPrototype :: Nil)
)
case class FeatureException(message: String)

View file

@ -226,13 +226,20 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
paymentId,
duration = 1.second)
received <- client4.audit()
relayed <- client2.audit()
sent <- client1.audit()
_ <- TestAsyncUtil.retryUntilSatisfiedF(
conditionF = () => client4.audit().map(_.received.nonEmpty),
interval = 1.second,
maxTries = 60)
_ <- TestAsyncUtil.retryUntilSatisfiedF(
conditionF = () => client2.audit().map(_.relayed.nonEmpty),
interval = 1.second,
maxTries = 60)
_ <- TestAsyncUtil.retryUntilSatisfiedF(
conditionF = () => client1.audit().map(_.sent.nonEmpty),
interval = 1.second,
maxTries = 60)
} yield {
assert(sent.sent.nonEmpty)
assert(received.received.nonEmpty)
assert(relayed.relayed.nonEmpty)
succeed
}
}
@ -964,11 +971,16 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
.map(_.collect { case open: OpenChannelInfo =>
open
})
ourChannelUpdates <- firstFreshClient.allUpdates(nodeId)
_ <- AsyncUtil
.retryUntilSatisfiedF(
(() => {
firstFreshClient
.allUpdates(nodeId)
.map(_.forall(updateIsInChannels(ourOpenChannels)))
}),
interval = 1.second,
maxTries = 60)
} yield {
assert(ourChannelUpdates.forall(updateIsInChannels(ourOpenChannels)))
succeed
}
}
@ -1161,10 +1173,16 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
c <- clientF
res <- c.listInvoices(from = None, to = Some(Instant.now()))
i <- c.createInvoice(description = "abc")
pending <- c.listPendingInvoices(from = None, to = None)
_ = Thread.sleep(1000 * 5)
_ <- AsyncUtil
.retryUntilSatisfiedF(
(() => {
c.listPendingInvoices(from = None, to = None).map(_.contains(i))
}),
interval = 1.second,
maxTries = 60)
} yield {
assert(res.nonEmpty)
assert(pending.contains(i))
assert(!res.contains(i))
}
}

View file

@ -19,8 +19,8 @@ TaskKeys.downloadEclair := {
Files.createDirectories(binaryDir)
}
val version = "0.7.0"
val commit = "a804905"
val version = "0.8.0"
val commit = "0077471"
logger.debug(s"(Maybe) downloading Eclair binaries for version: $version")
@ -48,7 +48,7 @@ TaskKeys.downloadEclair := {
.mkString
val expectedHash =
"482a00cc597fd4cc471a1b4035c72a440a9ab336ef5b9006629d2fd717b223b4"
"d279317de25ba86b275183160d83acd064647371c446a35601397ae87ee04abb"
val success = hash.equalsIgnoreCase(expectedHash)
if (success) {

View file

@ -272,7 +272,9 @@ trait EclairApi {
def onChainBalance(): Future[OnChainBalance]
def onChainTransactions(): Future[Vector[WalletTransaction]]
def onChainTransactions(
count: Int,
skip: Int): Future[Vector[WalletTransaction]]
def sendOnChain(
address: BitcoinAddress,

View file

@ -608,8 +608,12 @@ class EclairRpcClient(
eclairCall[OnChainBalance]("onchainbalance")
}
override def onChainTransactions(): Future[Vector[WalletTransaction]] = {
eclairCall[Vector[WalletTransaction]]("onchaintransactions")
override def onChainTransactions(
count: Int = 10,
skip: Int = 0): Future[Vector[WalletTransaction]] = {
eclairCall[Vector[WalletTransaction]]("onchaintransactions",
"count" -> count.toString,
"skip" -> skip.toString)
}
override def sendOnChain(
@ -953,13 +957,13 @@ object EclairRpcClient {
implicit system: ActorSystem) = new EclairRpcClient(instance, binary)
/** The current commit we support of Eclair */
private[bitcoins] val commit = "a804905"
private[bitcoins] val commit = "0077471"
/** The current version we support of Eclair */
private[bitcoins] val version = "0.7.0"
private[bitcoins] val version = "0.8.0"
/** The bitcoind version that eclair is officially tested & supported with by ACINQ
* @see https://github.com/ACINQ/eclair/releases/tag/v0.6.2
* @see https://github.com/ACINQ/eclair/releases/tag/v0.8.0
*/
val bitcoindV: BitcoindVersion = BitcoindVersion.V21
val bitcoindV: BitcoindVersion = BitcoindVersion.V23
}