@@ -5,10 +5,7 @@ import fr.acinq.bitcoin.ByteVector32
55import fr.acinq.bitcoin.PrivateKey
66import fr.acinq.bitcoin.PublicKey
77import fr.acinq.bitcoin.utils.Either
8- import fr.acinq.lightning.CltvExpiry
9- import fr.acinq.lightning.Feature
10- import fr.acinq.lightning.Lightning
11- import fr.acinq.lightning.MilliSatoshi
8+ import fr.acinq.lightning.*
129import fr.acinq.lightning.channel.ChannelCommand
1310import fr.acinq.lightning.crypto.RouteBlinding
1411import fr.acinq.lightning.crypto.sphinx.FailurePacket
@@ -59,6 +56,42 @@ object OutgoingPaymentPacket {
5956 return Triple (trampolineAmount, trampolineExpiry, paymentOnion)
6057 }
6158
59+ /* *
60+ * Build an encrypted payment onion packet when the final recipient supports trampoline.
61+ * We use each hop in the blinded path as a trampoline hop, which doesn't reveal anything to our trampoline node.
62+ * From their point of view, they will be relaying a trampoline payment to another trampoline node.
63+ * They won't even know that a blinded path is being used.
64+ */
65+ fun buildPacketToTrampolineRecipient (paymentHash : ByteVector32 , amount : MilliSatoshi , expiry : CltvExpiry , path : Bolt12Invoice .Companion .PaymentBlindedContactInfo , hop : NodeHop ): Triple <MilliSatoshi , CltvExpiry , PacketAndSecrets > {
66+ require(path.route.route.firstNodeId is EncodedNodeId .WithPublicKey ) { " blinded path must provide the introduction node_id" }
67+ val (trampolineAmount, trampolineExpiry, trampolineOnion) = run {
68+ val blindedAmount = amount + path.paymentInfo.fee(amount)
69+ val blindedExpiry = expiry + path.paymentInfo.cltvExpiryDelta
70+ val blindedNodes = listOf (path.route.route.firstNodeId.publicKey) + path.route.route.blindedNodeIds.drop(1 )
71+ val blindedPayloads = when {
72+ blindedNodes.size == 1 -> {
73+ val finalPayload = PaymentOnion .FinalPayload .Blinded .create(amount, expiry, path.route.route.encryptedPayloads.last(), path.route.route.firstPathKey)
74+ listOf (finalPayload)
75+ }
76+ else -> {
77+ val finalPayload = PaymentOnion .FinalPayload .Blinded .create(amount, expiry, path.route.route.encryptedPayloads.last(), pathKey = null )
78+ val intermediatePayloads = path.route.route.encryptedPayloads.drop(1 ).dropLast(1 ).map { PaymentOnion .BlindedChannelRelayPayload .create(it, pathKey = null ) }
79+ val introductionPayload = PaymentOnion .BlindedChannelRelayPayload .create(path.route.route.encryptedPayloads.first(), path.route.route.firstPathKey)
80+ listOf (introductionPayload) + intermediatePayloads + listOf (finalPayload)
81+ }
82+ }
83+ val trampolinePayload = PaymentOnion .NodeRelayPayload .create(blindedAmount, blindedExpiry, blindedNodes.first())
84+ val trampolineOnion = buildOnion(listOf (hop.nodeId) + blindedNodes, listOf (trampolinePayload) + blindedPayloads, paymentHash)
85+ val trampolineAmount = blindedAmount + hop.fee(blindedAmount)
86+ val trampolineExpiry = blindedExpiry + hop.cltvExpiryDelta
87+ Triple (trampolineAmount, trampolineExpiry, trampolineOnion)
88+ }
89+ val trampolinePaymentSecret = Lightning .randomBytes32()
90+ val payload = PaymentOnion .FinalPayload .Standard .createTrampolinePayload(trampolineAmount, trampolineAmount, trampolineExpiry, trampolinePaymentSecret, trampolineOnion.packet)
91+ val paymentOnion = buildOnion(listOf (hop.nodeId), listOf (payload), paymentHash, OnionRoutingPacket .PaymentPacketLength )
92+ return Triple (trampolineAmount, trampolineExpiry, paymentOnion)
93+ }
94+
6295 /* *
6396 * Build an encrypted payment onion packet when the final recipient is our trampoline node.
6497 *
0 commit comments