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