@@ -5,10 +5,7 @@ import fr.acinq.bitcoin.ByteVector32
5
5
import fr.acinq.bitcoin.PrivateKey
6
6
import fr.acinq.bitcoin.PublicKey
7
7
import 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.*
12
9
import fr.acinq.lightning.channel.ChannelCommand
13
10
import fr.acinq.lightning.crypto.sphinx.FailurePacket
14
11
import fr.acinq.lightning.crypto.sphinx.PacketAndSecrets
@@ -58,6 +55,42 @@ object OutgoingPaymentPacket {
58
55
return Triple (trampolineAmount, trampolineExpiry, paymentOnion)
59
56
}
60
57
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
+
61
94
/* *
62
95
* Build an encrypted payment onion packet when the final recipient is our trampoline node.
63
96
*
0 commit comments