commonMain.fr.acinq.lightning.json.JsonSerializers.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lightning-kmp-jvm Show documentation
Show all versions of lightning-kmp-jvm Show documentation
A Kotlin Multiplatform implementation of the Lightning Network
The newest version!
@file:OptIn(ExperimentalSerializationApi::class)
@file:UseSerializers(
// This is used by Kotlin at compile time to resolve serializers (defined in this file)
// in order to build serializers for other classes (also defined in this file).
// If we used @Serializable annotations directly on the actual classes, Kotlin would be
// able to resolve serializers by itself. It is verbose, but it allows us to contain
// serialization code in this file.
JsonSerializers.CommitmentSerializer::class,
JsonSerializers.CommitmentsSerializer::class,
JsonSerializers.ClosingFeeratesSerializer::class,
JsonSerializers.LocalParamsSerializer::class,
JsonSerializers.RemoteParamsSerializer::class,
JsonSerializers.LocalCommitSerializer::class,
JsonSerializers.RemoteCommitSerializer::class,
JsonSerializers.NextRemoteCommitSerializer::class,
JsonSerializers.LocalChangesSerializer::class,
JsonSerializers.RemoteChangesSerializer::class,
JsonSerializers.EitherSerializer::class,
JsonSerializers.ShaChainSerializer::class,
JsonSerializers.ByteVectorSerializer::class,
JsonSerializers.ByteVector32Serializer::class,
JsonSerializers.ByteVector64Serializer::class,
JsonSerializers.BlockHashSerializer::class,
JsonSerializers.PublicKeySerializer::class,
JsonSerializers.PrivateKeySerializer::class,
JsonSerializers.TxIdSerializer::class,
JsonSerializers.KeyPathSerializer::class,
JsonSerializers.SatoshiSerializer::class,
JsonSerializers.MilliSatoshiSerializer::class,
JsonSerializers.CltvExpirySerializer::class,
JsonSerializers.CltvExpiryDeltaSerializer::class,
JsonSerializers.FeeratePerKwSerializer::class,
JsonSerializers.CommitmentSpecSerializer::class,
JsonSerializers.PublishableTxsSerializer::class,
JsonSerializers.HtlcTxAndSigsSerializer::class,
JsonSerializers.ChannelConfigSerializer::class,
JsonSerializers.ChannelFeaturesSerializer::class,
JsonSerializers.FeaturesSerializer::class,
JsonSerializers.ShortChannelIdSerializer::class,
JsonSerializers.ChannelKeysSerializer::class,
JsonSerializers.TransactionSerializer::class,
JsonSerializers.OutPointSerializer::class,
JsonSerializers.TxOutSerializer::class,
JsonSerializers.ClosingTxProposedSerializer::class,
JsonSerializers.LocalCommitPublishedSerializer::class,
JsonSerializers.RemoteCommitPublishedSerializer::class,
JsonSerializers.RevokedCommitPublishedSerializer::class,
JsonSerializers.OnionRoutingPacketSerializer::class,
JsonSerializers.FundingSignedSerializer::class,
JsonSerializers.UpdateAddHtlcSerializer::class,
JsonSerializers.UpdateAddHtlcTlvSerializer::class,
JsonSerializers.UpdateAddHtlcTlvBlindingSerializer::class,
JsonSerializers.UpdateFulfillHtlcSerializer::class,
JsonSerializers.UpdateFailHtlcSerializer::class,
JsonSerializers.UpdateFailMalformedHtlcSerializer::class,
JsonSerializers.UpdateFeeSerializer::class,
JsonSerializers.ChannelUpdateSerializer::class,
JsonSerializers.ChannelAnnouncementSerializer::class,
JsonSerializers.WaitingForRevocationSerializer::class,
JsonSerializers.SharedFundingInputSerializer::class,
JsonSerializers.InteractiveTxParamsSerializer::class,
JsonSerializers.SignedSharedTransactionSerializer::class,
JsonSerializers.InteractiveTxSigningSessionSerializer::class,
JsonSerializers.RbfStatusSerializer::class,
JsonSerializers.SpliceStatusSerializer::class,
JsonSerializers.LiquidityLeaseFeesSerializer::class,
JsonSerializers.LiquidityLeaseWitnessSerializer::class,
JsonSerializers.LiquidityLeaseSerializer::class,
JsonSerializers.ChannelParamsSerializer::class,
JsonSerializers.ChannelOriginSerializer::class,
JsonSerializers.CommitmentChangesSerializer::class,
JsonSerializers.LocalFundingStatusSerializer::class,
JsonSerializers.RemoteFundingStatusSerializer::class,
JsonSerializers.ShutdownSerializer::class,
JsonSerializers.ClosingSignedSerializer::class,
JsonSerializers.CommitSigSerializer::class,
JsonSerializers.EncryptedChannelDataSerializer::class,
JsonSerializers.ChannelReestablishDataSerializer::class,
JsonSerializers.FundingCreatedSerializer::class,
JsonSerializers.ChannelReadySerializer::class,
JsonSerializers.ChannelReadyTlvShortChannelIdTlvSerializer::class,
JsonSerializers.ClosingSignedTlvFeeRangeSerializer::class,
JsonSerializers.ShutdownTlvChannelDataSerializer::class,
JsonSerializers.GenericTlvSerializer::class,
JsonSerializers.TlvStreamSerializer::class,
JsonSerializers.ShutdownTlvSerializer::class,
JsonSerializers.ClosingSignedTlvSerializer::class,
JsonSerializers.ChannelReestablishTlvSerializer::class,
JsonSerializers.ChannelReadyTlvSerializer::class,
JsonSerializers.CommitSigTlvAlternativeFeerateSigSerializer::class,
JsonSerializers.CommitSigTlvAlternativeFeerateSigsSerializer::class,
JsonSerializers.CommitSigTlvBatchSerializer::class,
JsonSerializers.CommitSigTlvSerializer::class,
JsonSerializers.UUIDSerializer::class,
JsonSerializers.ClosingSerializer::class,
JsonSerializers.Bolt11ExtraHopSerializer::class,
JsonSerializers.Bolt11UnknownTagSerializer::class,
JsonSerializers.Bolt11InvalidTagSerializer::class,
JsonSerializers.EncodedNodeIdSerializer::class,
JsonSerializers.BlindedNodeSerializer::class,
JsonSerializers.BlindedRouteSerializer::class,
)
@file:UseContextualSerialization(
PersistedChannelState::class
)
package fr.acinq.lightning.json
import fr.acinq.bitcoin.*
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.*
import fr.acinq.lightning.blockchain.fee.FeeratePerKw
import fr.acinq.lightning.channel.*
import fr.acinq.lightning.channel.states.*
import fr.acinq.lightning.crypto.KeyManager
import fr.acinq.lightning.crypto.RouteBlinding
import fr.acinq.lightning.crypto.ShaChain
import fr.acinq.lightning.json.JsonSerializers.LongSerializer
import fr.acinq.lightning.json.JsonSerializers.StringSerializer
import fr.acinq.lightning.json.JsonSerializers.SurrogateSerializer
import fr.acinq.lightning.payment.Bolt11Invoice
import fr.acinq.lightning.payment.Bolt11Invoice.TaggedField
import fr.acinq.lightning.transactions.CommitmentSpec
import fr.acinq.lightning.transactions.IncomingHtlc
import fr.acinq.lightning.transactions.OutgoingHtlc
import fr.acinq.lightning.utils.UUID
import fr.acinq.lightning.wire.*
import fr.acinq.lightning.wire.OfferTypes.OfferChains
import kotlinx.serialization.*
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.PolymorphicModuleBuilder
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.contextual
import kotlinx.serialization.modules.polymorphic
/**
* Json support for [ChannelState] based on `kotlinx-serialization`.
*
* The implementation is self-contained, all serializers are defined here as opposed to tagging
* our classes with `@Serializable`. Depending on the class to serialize, we use two techniques
* to define serializers:
* - `@Serializer(forClass = T::class)`, simplest method, equivalent to tagging T with `@Serializable`
* - [SurrogateSerializer] and its children ([StringSerializer], [LongSerializer]) for more complex cases
*
* Note that we manually have to define polymorphic classes in [SerializersModule], which would be done automatically
* by Kotlin if we directly tagged `sealed classes` with `@Serializable`.
*/
object JsonSerializers {
@OptIn(ExperimentalSerializationApi::class)
val json = Json {
prettyPrint = true
serializersModule = SerializersModule {
// we need to explicitly define a [PolymorphicSerializer] for sealed classes, but not for interfaces
fun PolymorphicModuleBuilder.registerChannelStateWithCommitmentsSubclasses() {
subclass(LegacyWaitForFundingConfirmed::class, LegacyWaitForFundingConfirmedSerializer)
subclass(LegacyWaitForFundingLocked::class, LegacyWaitForFundingLockedSerializer)
subclass(WaitForFundingConfirmed::class, WaitForFundingConfirmedSerializer)
subclass(WaitForChannelReady::class, WaitForChannelReadySerializer)
subclass(Normal::class, NormalSerializer)
subclass(ShuttingDown::class, ShuttingDownSerializer)
subclass(Negotiating::class, NegotiatingSerializer)
subclass(Closing::class, ClosingSerializer)
subclass(WaitForRemotePublishFutureCommitment::class, WaitForRemotePublishFutureCommitmentSerializer)
subclass(Closed::class, ClosedSerializer)
}
fun PolymorphicModuleBuilder.registerPersistedChannelStateSubclasses() {
registerChannelStateWithCommitmentsSubclasses()
subclass(WaitForFundingSigned::class, WaitForFundingSignedSerializer)
}
contextual(PolymorphicSerializer(ChannelState::class))
polymorphicDefaultSerializer(ChannelState::class) { ChannelStateSerializer }
polymorphic(ChannelState::class) {
registerPersistedChannelStateSubclasses()
subclass(Offline::class, OfflineSerializer)
subclass(Syncing::class, SyncingSerializer)
}
contextual(PolymorphicSerializer(PersistedChannelState::class))
polymorphicDefaultSerializer(PersistedChannelState::class) { ChannelStateSerializer }
polymorphic(PersistedChannelState::class) {
registerPersistedChannelStateSubclasses()
}
contextual(PolymorphicSerializer(ChannelStateWithCommitments::class))
polymorphicDefaultSerializer(ChannelStateWithCommitments::class) { ChannelStateSerializer }
polymorphic(ChannelStateWithCommitments::class) {
registerChannelStateWithCommitmentsSubclasses()
}
polymorphic(UpdateMessage::class) {
subclass(UpdateAddHtlc::class, UpdateAddHtlcSerializer)
subclass(UpdateFailHtlc::class, UpdateFailHtlcSerializer)
subclass(UpdateFailMalformedHtlc::class, UpdateFailMalformedHtlcSerializer)
subclass(UpdateFulfillHtlc::class, UpdateFulfillHtlcSerializer)
subclass(UpdateFee::class, UpdateFeeSerializer)
}
polymorphic(Tlv::class) {
subclass(ChannelReadyTlv.ShortChannelIdTlv::class, ChannelReadyTlvShortChannelIdTlvSerializer)
subclass(CommitSigTlv.AlternativeFeerateSigs::class, CommitSigTlvAlternativeFeerateSigsSerializer)
subclass(CommitSigTlv.Batch::class, CommitSigTlvBatchSerializer)
subclass(ShutdownTlv.ChannelData::class, ShutdownTlvChannelDataSerializer)
subclass(ClosingSignedTlv.FeeRange::class, ClosingSignedTlvFeeRangeSerializer)
subclass(UpdateAddHtlcTlv.Blinding::class, UpdateAddHtlcTlvBlindingSerializer)
}
// TODO The following declarations are required because serializers for [TransactionWithInputInfo]
// depend themselves on @Contextual serializers. Once we get rid of v2/v3 serialization and we
// define our own context-less serializers in this file, we will be able to clean up
// those declarations.
contextual(OutPointSerializer)
contextual(TxOutSerializer)
contextual(TransactionSerializer)
contextual(ByteVectorSerializer)
contextual(ByteVector32Serializer)
contextual(Bolt11InvoiceSerializer)
contextual(OfferSerializer)
}
}
@Serializable
data class ChannelStateSurrogateSerializer(val forState: String)
object ChannelStateSerializer : SurrogateSerializer(
transform = { ChannelStateSurrogateSerializer(it::class.qualifiedName!!) },
delegateSerializer = ChannelStateSurrogateSerializer.serializer()
)
@Serializer(forClass = Offline::class)
object OfflineSerializer
@Serializer(forClass = Syncing::class)
object SyncingSerializer
@Serializer(forClass = LegacyWaitForFundingConfirmed::class)
object LegacyWaitForFundingConfirmedSerializer
@Serializer(forClass = LegacyWaitForFundingLocked::class)
object LegacyWaitForFundingLockedSerializer
@Serializer(forClass = WaitForFundingSigned::class)
object WaitForFundingSignedSerializer
@Serializer(forClass = WaitForFundingConfirmed::class)
object WaitForFundingConfirmedSerializer
@Serializer(forClass = WaitForChannelReady::class)
object WaitForChannelReadySerializer
@Serializer(forClass = Normal::class)
object NormalSerializer
@Serializer(forClass = ShuttingDown::class)
object ShuttingDownSerializer
@Serializer(forClass = Negotiating::class)
object NegotiatingSerializer
@Serializer(forClass = Closing::class)
object ClosingSerializer
@Serializer(forClass = WaitForRemotePublishFutureCommitment::class)
object WaitForRemotePublishFutureCommitmentSerializer
@Serializer(forClass = Closed::class)
object ClosedSerializer
@Serializable
data class SharedFundingInputSurrogate(val outPoint: OutPoint, val amount: Satoshi)
object SharedFundingInputSerializer : SurrogateSerializer(
transform = { i ->
when (i) {
is SharedFundingInput.Multisig2of2 -> SharedFundingInputSurrogate(i.info.outPoint, i.info.txOut.amount)
}
},
delegateSerializer = SharedFundingInputSurrogate.serializer()
)
@Serializer(forClass = InteractiveTxParams::class)
object InteractiveTxParamsSerializer
@Serializer(forClass = SignedSharedTransaction::class)
object SignedSharedTransactionSerializer
@Serializable
data class InteractiveTxSigningSessionSurrogate(val fundingParams: InteractiveTxParams, val fundingTxId: TxId)
object InteractiveTxSigningSessionSerializer : SurrogateSerializer(
transform = { s -> InteractiveTxSigningSessionSurrogate(s.fundingParams, s.fundingTx.txId) },
delegateSerializer = InteractiveTxSigningSessionSurrogate.serializer()
)
@Serializer(forClass = RbfStatus::class)
object RbfStatusSerializer
object SpliceStatusSerializer : StringSerializer({ it::class.simpleName!! })
@Serializer(forClass = LiquidityAds.LeaseFees::class)
object LiquidityLeaseFeesSerializer
@Serializer(forClass = LiquidityAds.LeaseWitness::class)
object LiquidityLeaseWitnessSerializer
@Serializer(forClass = LiquidityAds.Lease::class)
object LiquidityLeaseSerializer
@Serializer(forClass = ChannelParams::class)
object ChannelParamsSerializer
@Serializer(forClass = Origin::class)
object ChannelOriginSerializer
@Serializer(forClass = CommitmentChanges::class)
object CommitmentChangesSerializer
@Serializable
data class LocalFundingStatusSurrogate(val status: String, val txId: TxId)
object LocalFundingStatusSerializer : SurrogateSerializer(
transform = { o ->
when (o) {
is LocalFundingStatus.UnconfirmedFundingTx -> LocalFundingStatusSurrogate("unconfirmed", o.txId)
is LocalFundingStatus.ConfirmedFundingTx -> LocalFundingStatusSurrogate("confirmed", o.txId)
}
},
delegateSerializer = LocalFundingStatusSurrogate.serializer()
)
@Serializable
data class RemoteFundingStatusSurrogate(val status: String)
object RemoteFundingStatusSerializer : SurrogateSerializer(
transform = { o ->
when (o) {
RemoteFundingStatus.NotLocked -> RemoteFundingStatusSurrogate("not-locked")
RemoteFundingStatus.Locked -> RemoteFundingStatusSurrogate("locked")
}
},
delegateSerializer = RemoteFundingStatusSurrogate.serializer()
)
@Serializer(forClass = Commitment::class)
object CommitmentSerializer
@Serializer(forClass = Commitments::class)
object CommitmentsSerializer
@Serializer(forClass = ClosingFeerates::class)
object ClosingFeeratesSerializer
@Serializer(forClass = LocalParams::class)
object LocalParamsSerializer
@Serializer(forClass = RemoteParams::class)
object RemoteParamsSerializer
@Serializer(forClass = LocalCommit::class)
object LocalCommitSerializer
@Serializer(forClass = RemoteCommit::class)
object RemoteCommitSerializer
@Serializer(forClass = NextRemoteCommit::class)
object NextRemoteCommitSerializer
@Serializer(forClass = LocalChanges::class)
object LocalChangesSerializer
@Serializer(forClass = RemoteChanges::class)
object RemoteChangesSerializer
/**
* Delegate serialization of type [T] to serialization of type [S].
* @param transform a conversion method from [T] to [S]
* @param delegateSerializer serializer for [S]
*/
sealed class SurrogateSerializer(val transform: (T) -> S, private val delegateSerializer: KSerializer) : KSerializer {
override val descriptor: SerialDescriptor get() = delegateSerializer.descriptor
override fun serialize(encoder: Encoder, value: T) = delegateSerializer.serialize(encoder, transform(value))
override fun deserialize(decoder: Decoder): T = TODO("json deserialization is not supported")
}
sealed class StringSerializer(toString: (T) -> String = { it.toString() }) : SurrogateSerializer(toString, String.serializer())
sealed class LongSerializer(toLong: (T) -> Long) : SurrogateSerializer(toLong, Long.serializer())
object ShaChainSerializer : StringSerializer({ "" })
object PrivateKeySerializer : StringSerializer({ "" })
object OnionRoutingPacketSerializer : StringSerializer({ "" })
object ByteVectorSerializer : StringSerializer()
object ByteVector32Serializer : StringSerializer()
object ByteVector64Serializer : StringSerializer()
object BlockHashSerializer : StringSerializer()
object PublicKeySerializer : StringSerializer()
object TxIdSerializer : StringSerializer()
object KeyPathSerializer : StringSerializer()
object ShortChannelIdSerializer : StringSerializer()
object OutPointSerializer : StringSerializer({ "${it.txid}:${it.index}" })
object TransactionSerializer : StringSerializer()
@Serializer(forClass = PublishableTxs::class)
object PublishableTxsSerializer
@Serializable
data class CommitmentsSpecSurrogate(val htlcsIn: List, val htlcsOut: List, val feerate: FeeratePerKw, val toLocal: MilliSatoshi, val toRemote: MilliSatoshi)
object CommitmentSpecSerializer : SurrogateSerializer(
transform = { o ->
CommitmentsSpecSurrogate(
htlcsIn = o.htlcs.filterIsInstance().map { it.add },
htlcsOut = o.htlcs.filterIsInstance().map { it.add },
feerate = o.feerate, toLocal = o.toLocal, toRemote = o.toRemote
)
},
delegateSerializer = CommitmentsSpecSurrogate.serializer()
)
@Serializer(forClass = HtlcTxAndSigs::class)
object HtlcTxAndSigsSerializer
object ChannelConfigSerializer : SurrogateSerializer>(
transform = { o -> o.options.map { it.name } },
delegateSerializer = ListSerializer(String.serializer())
)
object ChannelFeaturesSerializer : SurrogateSerializer>(
transform = { o -> o.features.map { it.rfcName } },
delegateSerializer = ListSerializer(String.serializer())
)
@Serializable
data class FeaturesSurrogate(val activated: Map, val unknown: Set)
object FeaturesSerializer : SurrogateSerializer(
transform = { o -> FeaturesSurrogate(o.activated.map { it.key.rfcName to it.value.name }.toMap(), o.unknown.map { it.bitIndex }.toSet()) },
delegateSerializer = FeaturesSurrogate.serializer()
)
@Serializable
data class TxOutSurrogate(val amount: Satoshi, val publicKeyScript: ByteVector)
object TxOutSerializer : SurrogateSerializer(
transform = { o -> TxOutSurrogate(o.amount, o.publicKeyScript) },
delegateSerializer = TxOutSurrogate.serializer()
)
object SatoshiSerializer : LongSerializer({ it.toLong() })
object MilliSatoshiSerializer : LongSerializer({ it.toLong() })
object CltvExpirySerializer : LongSerializer({ it.toLong() })
object CltvExpiryDeltaSerializer : LongSerializer({ it.toLong() })
object FeeratePerKwSerializer : LongSerializer({ it.toLong() })
object ChannelKeysSerializer : SurrogateSerializer(
transform = { it.fundingKeyPath },
delegateSerializer = KeyPathSerializer
)
@Serializer(forClass = ClosingTxProposed::class)
object ClosingTxProposedSerializer
@Serializer(forClass = LocalCommitPublished::class)
object LocalCommitPublishedSerializer
@Serializer(forClass = RemoteCommitPublished::class)
object RemoteCommitPublishedSerializer
@Serializer(forClass = RevokedCommitPublished::class)
object RevokedCommitPublishedSerializer
@Serializer(forClass = FundingSigned::class)
object FundingSignedSerializer
@Serializer(forClass = EncryptedChannelData::class)
object EncryptedChannelDataSerializer
@Serializer(forClass = UpdateAddHtlcTlv.Blinding::class)
object UpdateAddHtlcTlvBlindingSerializer
@Serializer(forClass = UpdateAddHtlcTlv::class)
object UpdateAddHtlcTlvSerializer
@Serializer(forClass = UpdateAddHtlc::class)
object UpdateAddHtlcSerializer
@Serializer(forClass = UpdateFulfillHtlc::class)
object UpdateFulfillHtlcSerializer
@Serializer(forClass = UpdateFailHtlc::class)
object UpdateFailHtlcSerializer
@Serializer(forClass = UpdateFailMalformedHtlc::class)
object UpdateFailMalformedHtlcSerializer
@Serializer(forClass = UpdateFee::class)
object UpdateFeeSerializer
@Serializer(forClass = ChannelUpdate::class)
object ChannelUpdateSerializer
@Serializer(forClass = ChannelAnnouncement::class)
object ChannelAnnouncementSerializer
@Serializer(forClass = Shutdown::class)
object ShutdownSerializer
@Serializer(forClass = ClosingSigned::class)
object ClosingSignedSerializer
@Serializer(forClass = CommitSig::class)
object CommitSigSerializer
@Serializer(forClass = ChannelReestablish::class)
object ChannelReestablishDataSerializer
@Serializer(forClass = FundingCreated::class)
object FundingCreatedSerializer
@Serializer(forClass = ChannelReady::class)
object ChannelReadySerializer
@Serializer(forClass = ChannelReadyTlv.ShortChannelIdTlv::class)
object ChannelReadyTlvShortChannelIdTlvSerializer
@Serializer(forClass = ClosingSignedTlv.FeeRange::class)
object ClosingSignedTlvFeeRangeSerializer
@Serializer(forClass = ShutdownTlv.ChannelData::class)
object ShutdownTlvChannelDataSerializer
@Serializer(forClass = ShutdownTlv::class)
object ShutdownTlvSerializer
@Serializer(forClass = CommitSigTlv.AlternativeFeerateSig::class)
object CommitSigTlvAlternativeFeerateSigSerializer
@Serializer(forClass = CommitSigTlv.AlternativeFeerateSigs::class)
object CommitSigTlvAlternativeFeerateSigsSerializer
@Serializer(forClass = CommitSigTlv.Batch::class)
object CommitSigTlvBatchSerializer
@Serializer(forClass = CommitSigTlv::class)
object CommitSigTlvSerializer
@Serializer(forClass = ClosingSignedTlv::class)
object ClosingSignedTlvSerializer
@Serializer(forClass = ChannelReadyTlv::class)
object ChannelReadyTlvSerializer
@Serializer(forClass = ChannelReestablishTlv::class)
object ChannelReestablishTlvSerializer
@Serializer(forClass = GenericTlv::class)
object GenericTlvSerializer
@Serializable
data class TlvStreamSurrogate(val records: Set, val unknown: Set = setOf())
class TlvStreamSerializer : KSerializer> {
private val delegateSerializer = TlvStreamSurrogate.serializer()
override val descriptor: SerialDescriptor = delegateSerializer.descriptor
override fun serialize(encoder: Encoder, value: TlvStream) =
delegateSerializer.serialize(encoder, TlvStreamSurrogate(value.records, value.unknown))
override fun deserialize(decoder: Decoder): TlvStream = TODO("json deserialization is not supported")
}
class EitherSerializer(val aSer: KSerializer, val bSer: KSerializer) : KSerializer> {
@Serializable
data class Surrogate(val left: A?, val right: B?)
override val descriptor = Surrogate.serializer(aSer, bSer).descriptor
override fun serialize(encoder: Encoder, value: Either) {
val surrogate = Surrogate(value.left, value.right)
return encoder.encodeSerializableValue(Surrogate.serializer(aSer, bSer), surrogate)
}
override fun deserialize(decoder: Decoder): Either = TODO("json deserialization is not supported")
}
@Serializer(forClass = WaitingForRevocation::class)
object WaitingForRevocationSerializer
object UUIDSerializer : StringSerializer()
@Serializer(forClass = TaggedField.ExtraHop::class)
object Bolt11ExtraHopSerializer
@Serializer(forClass = TaggedField.UnknownTag::class)
object Bolt11UnknownTagSerializer
@Serializer(forClass = TaggedField.InvalidTag::class)
object Bolt11InvalidTagSerializer
@Serializable
data class Bolt11InvoiceSurrogate(
val chain: String,
val amount: MilliSatoshi?,
val paymentHash: ByteVector32,
val description: String?,
val descriptionHash: ByteVector32?,
val minFinalCltvExpiryDelta: CltvExpiryDelta?,
val paymentSecret: ByteVector32,
val paymentMetadata: ByteVector?,
val expirySeconds: Long?,
val extraHops: List>,
val features: Features?,
val timestampSeconds: Long,
val unknownTags: List?,
val invalidTags: List?,
)
object Bolt11InvoiceSerializer : SurrogateSerializer(
transform = { o ->
Bolt11InvoiceSurrogate(
chain = o.chain?.name?.lowercase() ?: "unknown",
amount = o.amount,
paymentHash = o.paymentHash,
description = o.description,
descriptionHash = o.descriptionHash,
minFinalCltvExpiryDelta = o.minFinalExpiryDelta,
paymentSecret = o.paymentSecret,
paymentMetadata = o.paymentMetadata,
expirySeconds = o.expirySeconds,
extraHops = o.routingInfo.map { it.hints },
features = o.features.let { if (it == Features.empty) null else it },
timestampSeconds = o.timestampSeconds,
unknownTags = o.tags.filterIsInstance().run { ifEmpty { null } },
invalidTags = o.tags.filterIsInstance().run { ifEmpty { null } }
)
},
delegateSerializer = Bolt11InvoiceSurrogate.serializer()
)
@Serializable
data class EncodedNodeIdSurrogate(val isNode1: Boolean?, val scid: ShortChannelId?, val publicKey: PublicKey?, val walletPublicKey: PublicKey?)
object EncodedNodeIdSerializer : SurrogateSerializer(
transform = { o ->
when (o) {
is EncodedNodeId.WithPublicKey.Plain -> EncodedNodeIdSurrogate(isNode1 = null, scid = null, publicKey = o.publicKey, walletPublicKey = null)
is EncodedNodeId.WithPublicKey.Wallet -> EncodedNodeIdSurrogate(isNode1 = null, scid = null, publicKey = null, walletPublicKey = o.publicKey)
is EncodedNodeId.ShortChannelIdDir -> EncodedNodeIdSurrogate(isNode1 = o.isNode1, scid = o.scid, publicKey = null, walletPublicKey = null)
}
},
delegateSerializer = EncodedNodeIdSurrogate.serializer()
)
@Serializer(forClass = RouteBlinding.BlindedNode::class)
object BlindedNodeSerializer
@Serializer(forClass = RouteBlinding.BlindedRoute::class)
object BlindedRouteSerializer
@Serializable
data class OfferSurrogate(
val chain: String,
val chainHashes: List?,
val amount: MilliSatoshi?,
val currency: String?,
val issuer: String?,
val quantityMax: Long?,
val description: String?,
val metadata: ByteVector?,
val expirySeconds: Long?,
val nodeId: PublicKey?,
val path: List?,
val features: Features?,
val unknownTlvs: List?
)
object OfferSerializer : SurrogateSerializer(
transform = { o ->
OfferSurrogate(
chain = when {
o.chains.isEmpty() -> Chain.Mainnet.name
o.chains.contains(Chain.Mainnet.chainHash) && o.chains.size == 1 -> Chain.Mainnet.name
o.chains.contains(Chain.Testnet.chainHash) && o.chains.size == 1 -> Chain.Testnet.name
o.chains.contains(Chain.Regtest.chainHash) && o.chains.size == 1 -> Chain.Regtest.name
else -> "unknown"
}.lowercase(),
chainHashes = o.records.get()?.chains,
amount = o.amount,
currency = o.currency,
issuer = o.issuer,
quantityMax = o.quantityMax,
description = o.description,
metadata = o.metadata,
expirySeconds = o.expirySeconds,
nodeId = o.nodeId,
path = o.paths?.map { it.route }?.run { ifEmpty { null } },
features = o.features.let { if (it == Features.empty) null else it },
unknownTlvs = o.records.unknown.toList().run { ifEmpty { null } }
)
},
delegateSerializer = OfferSurrogate.serializer()
)
}