mirror of
https://github.com/ACINQ/eclair.git
synced 2025-02-20 13:34:35 +01:00
Add some channel events to websocket (#1536)
Send basic channel events to websockets listeners: * Channel open initiated * Channel state change * Channel closed We only send basic, high-level data about these events. If the listener is interested in details, it should call the `channelInfo` API to get all of the channel's data. Fixes #1509
This commit is contained in:
parent
483cce4ae2
commit
42481c66e6
3 changed files with 62 additions and 4 deletions
|
@ -25,7 +25,7 @@ import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey}
|
|||
import fr.acinq.bitcoin.{ByteVector32, ByteVector64, OutPoint, Satoshi, Transaction}
|
||||
import fr.acinq.eclair.ApiTypes.ChannelIdentifier
|
||||
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
||||
import fr.acinq.eclair.channel.{ChannelOpenResponse, ChannelVersion, CloseCommand, Command, CommandResponse, RES_SUCCESS, State}
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.crypto.ShaChain
|
||||
import fr.acinq.eclair.db.{IncomingPaymentStatus, OutgoingPaymentStatus}
|
||||
import fr.acinq.eclair.payment._
|
||||
|
@ -42,6 +42,7 @@ import scodec.bits.ByteVector
|
|||
* JSON Serializers.
|
||||
* Note: in general, deserialization does not need to be implemented.
|
||||
*/
|
||||
|
||||
class ByteVectorSerializer extends CustomSerializer[ByteVector](_ => ( {
|
||||
null
|
||||
}, {
|
||||
|
@ -278,6 +279,30 @@ class JavaUUIDSerializer extends CustomSerializer[UUID](_ => ( {
|
|||
case id: UUID => JString(id.toString)
|
||||
}))
|
||||
|
||||
class ChannelEventSerializer extends CustomSerializer[ChannelEvent](_ => ( {
|
||||
null
|
||||
}, {
|
||||
case e: ChannelCreated => JObject(
|
||||
JField("type", JString("channel-opened")),
|
||||
JField("remoteNodeId", JString(e.remoteNodeId.toString())),
|
||||
JField("isFunder", JBool(e.isFunder)),
|
||||
JField("temporaryChannelId", JString(e.temporaryChannelId.toHex)),
|
||||
JField("initialFeeratePerKw", JLong(e.initialFeeratePerKw.toLong)),
|
||||
JField("fundingTxFeeratePerKw", e.fundingTxFeeratePerKw.map(f => JLong(f.toLong)).getOrElse(JNothing))
|
||||
)
|
||||
case e: ChannelStateChanged => JObject(
|
||||
JField("type", JString("channel-state-changed")),
|
||||
JField("remoteNodeId", JString(e.remoteNodeId.toString())),
|
||||
JField("previousState", JString(e.previousState.toString)),
|
||||
JField("currentState", JString(e.currentState.toString))
|
||||
)
|
||||
case e: ChannelClosed => JObject(
|
||||
JField("type", JString("channel-closed")),
|
||||
JField("channelId", JString(e.channelId.toHex)),
|
||||
JField("closingType", JString(e.closingType.getClass.getSimpleName))
|
||||
)
|
||||
}))
|
||||
|
||||
case class CustomTypeHints(custom: Map[Class[_], String]) extends TypeHints {
|
||||
val reverse: Map[String, Class[_]] = custom.map(_.swap)
|
||||
|
||||
|
@ -321,6 +346,7 @@ object JsonSupport extends Json4sSupport {
|
|||
new ByteVectorSerializer +
|
||||
new ByteVector32Serializer +
|
||||
new ByteVector64Serializer +
|
||||
new ChannelEventSerializer +
|
||||
new UInt64Serializer +
|
||||
new SatoshiSerializer +
|
||||
new MilliSatoshiSerializer +
|
||||
|
@ -360,10 +386,12 @@ object JsonSupport extends Json4sSupport {
|
|||
JObject(
|
||||
JField("name", JString(a.feature.rfcName)),
|
||||
JField("support", JString(a.support.toString))
|
||||
)}.toList)),
|
||||
)
|
||||
}.toList)),
|
||||
JField("unknown", JArray(features.unknown.map { i =>
|
||||
JObject(
|
||||
JField("featureBit", JInt(i.bitIndex))
|
||||
)}.toList))
|
||||
)
|
||||
}.toList))
|
||||
)
|
||||
}
|
|
@ -35,6 +35,7 @@ import fr.acinq.bitcoin.Crypto.PublicKey
|
|||
import fr.acinq.bitcoin.{ByteVector32, Satoshi}
|
||||
import fr.acinq.eclair.api.FormParamExtractors._
|
||||
import fr.acinq.eclair.blockchain.fee.FeeratePerByte
|
||||
import fr.acinq.eclair.channel.{ChannelClosed, ChannelCreated, ChannelEvent, ChannelStateChanged, WAIT_FOR_INIT_INTERNAL}
|
||||
import fr.acinq.eclair.io.NodeURI
|
||||
import fr.acinq.eclair.payment.{PaymentEvent, PaymentRequest}
|
||||
import fr.acinq.eclair.{CltvExpiryDelta, Eclair, MilliSatoshi}
|
||||
|
@ -91,10 +92,19 @@ trait Service extends ExtraDirectives with Logging {
|
|||
|
||||
override def preStart: Unit = {
|
||||
context.system.eventStream.subscribe(self, classOf[PaymentEvent])
|
||||
context.system.eventStream.subscribe(self, classOf[ChannelCreated])
|
||||
context.system.eventStream.subscribe(self, classOf[ChannelStateChanged])
|
||||
context.system.eventStream.subscribe(self, classOf[ChannelClosed])
|
||||
}
|
||||
|
||||
def receive: Receive = {
|
||||
case message: PaymentEvent => flowInput.offer(serialization.write(message))
|
||||
case message: ChannelCreated => flowInput.offer(serialization.write(message))
|
||||
case message: ChannelStateChanged =>
|
||||
if (message.previousState != WAIT_FOR_INIT_INTERNAL) {
|
||||
flowInput.offer(serialization.write(message))
|
||||
}
|
||||
case message: ChannelClosed => flowInput.offer(serialization.write(message))
|
||||
}
|
||||
|
||||
}))
|
||||
|
|
|
@ -33,7 +33,9 @@ import fr.acinq.eclair.ApiTypes.ChannelIdentifier
|
|||
import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
|
||||
import fr.acinq.eclair.Features.{ChannelRangeQueriesExtended, OptionDataLossProtect}
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.channel.{CMD_CLOSE, ChannelOpenResponse, CommandResponse, RES_SUCCESS}
|
||||
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
||||
import fr.acinq.eclair.channel.Helpers.Closing
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.db._
|
||||
import fr.acinq.eclair.io.NodeURI
|
||||
import fr.acinq.eclair.io.Peer.PeerInfo
|
||||
|
@ -530,6 +532,24 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
|
|||
assert(serialization.write(pset) === expectedSerializedPset)
|
||||
system.eventStream.publish(pset)
|
||||
wsClient.expectMessage(expectedSerializedPset)
|
||||
|
||||
val chcr = ChannelCreated(system.deadLetters, system.deadLetters, bobNodeId, isFunder = true, ByteVector32.One, FeeratePerKw(25 sat), Some(FeeratePerKw(20 sat)))
|
||||
val expectedSerializedChcr = """{"type":"channel-opened","remoteNodeId":"039dc0e0b1d25905e44fdf6f8e89755a5e219685840d0bc1d28d3308f9628a3585","isFunder":true,"temporaryChannelId":"0100000000000000000000000000000000000000000000000000000000000000","initialFeeratePerKw":25,"fundingTxFeeratePerKw":20}"""
|
||||
assert(serialization.write(chcr) === expectedSerializedChcr)
|
||||
system.eventStream.publish(chcr)
|
||||
wsClient.expectMessage(expectedSerializedChcr)
|
||||
|
||||
val chsc = ChannelStateChanged(system.deadLetters, system.deadLetters, bobNodeId, OFFLINE, NORMAL, null)
|
||||
val expectedSerializedChsc = """{"type":"channel-state-changed","remoteNodeId":"039dc0e0b1d25905e44fdf6f8e89755a5e219685840d0bc1d28d3308f9628a3585","previousState":"OFFLINE","currentState":"NORMAL"}"""
|
||||
assert(serialization.write(chsc) === expectedSerializedChsc)
|
||||
system.eventStream.publish(chsc)
|
||||
wsClient.expectMessage(expectedSerializedChsc)
|
||||
|
||||
val chcl = ChannelClosed(system.deadLetters, ByteVector32.One, Closing.NextRemoteClose(null, null), null)
|
||||
val expectedSerializedChcl = """{"type":"channel-closed","channelId":"0100000000000000000000000000000000000000000000000000000000000000","closingType":"NextRemoteClose"}"""
|
||||
assert(serialization.write(chcl) === expectedSerializedChcl)
|
||||
system.eventStream.publish(chcl)
|
||||
wsClient.expectMessage(expectedSerializedChcl)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue