16
16
17
17
package fr .acinq .eclair .io
18
18
19
- import akka .actor .typed .Behavior
20
19
import akka .actor .typed .eventstream .EventStream
21
20
import akka .actor .typed .scaladsl .adapter .TypedActorRefOps
22
21
import akka .actor .typed .scaladsl .{ActorContext , Behaviors }
22
+ import akka .actor .typed .{Behavior , SupervisorStrategy }
23
23
import akka .actor .{ActorRef , typed }
24
24
import fr .acinq .bitcoin .scalacompat .ByteVector32
25
25
import fr .acinq .bitcoin .scalacompat .Crypto .PublicKey
@@ -32,6 +32,8 @@ import fr.acinq.eclair.router.Router
32
32
import fr .acinq .eclair .wire .protocol .OnionMessage
33
33
import fr .acinq .eclair .{EncodedNodeId , NodeParams , ShortChannelId }
34
34
35
+ import scala .concurrent .duration .DurationInt
36
+
35
37
object MessageRelay {
36
38
// @formatter:off
37
39
sealed trait Command
@@ -42,29 +44,18 @@ object MessageRelay {
42
44
policy : RelayPolicy ,
43
45
replyTo_opt : Option [typed.ActorRef [Status ]]) extends Command
44
46
case class WrappedPeerInfo (peerInfo : PeerInfoResponse ) extends Command
45
- case class WrappedConnectionResult (result : PeerConnection .ConnectionResult ) extends Command
46
- case class WrappedOptionalNodeId (nodeId_opt : Option [PublicKey ]) extends Command
47
+ private case class WrappedConnectionResult (result : PeerConnection .ConnectionResult ) extends Command
48
+ private case class WrappedOptionalNodeId (nodeId_opt : Option [PublicKey ]) extends Command
49
+ private case class WrappedPeerReadyResult (result : PeerReadyNotifier .Result ) extends Command
47
50
48
- sealed trait Status {
49
- val messageId : ByteVector32
50
- }
51
+ sealed trait Status { val messageId : ByteVector32 }
51
52
case class Sent (messageId : ByteVector32 ) extends Status
52
53
sealed trait Failure extends Status
53
- case class AgainstPolicy (messageId : ByteVector32 , policy : RelayPolicy ) extends Failure {
54
- override def toString : String = s " Relay prevented by policy $policy"
55
- }
56
- case class ConnectionFailure (messageId : ByteVector32 , failure : PeerConnection .ConnectionResult .Failure ) extends Failure {
57
- override def toString : String = s " Can't connect to peer: ${failure.toString}"
58
- }
59
- case class Disconnected (messageId : ByteVector32 ) extends Failure {
60
- override def toString : String = " Peer is not connected"
61
- }
62
- case class UnknownChannel (messageId : ByteVector32 , channelId : ShortChannelId ) extends Failure {
63
- override def toString : String = s " Unknown channel: $channelId"
64
- }
65
- case class DroppedMessage (messageId : ByteVector32 , reason : DropReason ) extends Failure {
66
- override def toString : String = s " Message dropped: $reason"
67
- }
54
+ case class AgainstPolicy (messageId : ByteVector32 , policy : RelayPolicy ) extends Failure { override def toString : String = s " Relay prevented by policy $policy" }
55
+ case class ConnectionFailure (messageId : ByteVector32 , failure : PeerConnection .ConnectionResult .Failure ) extends Failure { override def toString : String = s " Can't connect to peer: ${failure.toString}" }
56
+ case class Disconnected (messageId : ByteVector32 ) extends Failure { override def toString : String = " Peer is not connected" }
57
+ case class UnknownChannel (messageId : ByteVector32 , channelId : ShortChannelId ) extends Failure { override def toString : String = s " Unknown channel: $channelId" }
58
+ case class DroppedMessage (messageId : ByteVector32 , reason : DropReason ) extends Failure { override def toString : String = s " Message dropped: $reason" }
68
59
69
60
sealed trait RelayPolicy
70
61
case object RelayChannelsOnly extends RelayPolicy
@@ -100,15 +91,15 @@ private class MessageRelay(nodeParams: NodeParams,
100
91
def queryNextNodeId (msg : OnionMessage , nextNode : Either [ShortChannelId , EncodedNodeId ]): Behavior [Command ] = {
101
92
nextNode match {
102
93
case Left (outgoingChannelId) if outgoingChannelId == ShortChannelId .toSelf =>
103
- withNextNodeId(msg, nodeParams.nodeId)
94
+ withNextNodeId(msg, EncodedNodeId . WithPublicKey . Plain ( nodeParams.nodeId) )
104
95
case Left (outgoingChannelId) =>
105
96
register ! Register .GetNextNodeId (context.messageAdapter(WrappedOptionalNodeId ), outgoingChannelId)
106
97
waitForNextNodeId(msg, outgoingChannelId)
107
98
case Right (EncodedNodeId .ShortChannelIdDir (isNode1, scid)) =>
108
99
router ! Router .GetNodeId (context.messageAdapter(WrappedOptionalNodeId ), scid, isNode1)
109
100
waitForNextNodeId(msg, scid)
110
101
case Right (encodedNodeId : EncodedNodeId .WithPublicKey ) =>
111
- withNextNodeId(msg, encodedNodeId.publicKey )
102
+ withNextNodeId(msg, encodedNodeId)
112
103
}
113
104
}
114
105
@@ -118,33 +109,39 @@ private class MessageRelay(nodeParams: NodeParams,
118
109
replyTo_opt.foreach(_ ! UnknownChannel (messageId, channelId))
119
110
Behaviors .stopped
120
111
case WrappedOptionalNodeId (Some (nextNodeId)) =>
121
- withNextNodeId(msg, nextNodeId)
112
+ withNextNodeId(msg, EncodedNodeId . WithPublicKey . Plain ( nextNodeId) )
122
113
}
123
114
}
124
115
125
- private def withNextNodeId (msg : OnionMessage , nextNodeId : PublicKey ): Behavior [Command ] = {
126
- if (nextNodeId == nodeParams.nodeId) {
127
- OnionMessages .process(nodeParams.privateKey, msg) match {
128
- case OnionMessages .DropMessage (reason) =>
129
- replyTo_opt.foreach(_ ! DroppedMessage (messageId, reason))
130
- Behaviors .stopped
131
- case OnionMessages .SendMessage (nextNode, nextMessage) =>
132
- // We need to repeat the process until we identify the (real) next node, or find out that we're the recipient.
133
- queryNextNodeId(nextMessage, nextNode)
134
- case received : OnionMessages .ReceiveMessage =>
135
- context.system.eventStream ! EventStream .Publish (received)
136
- replyTo_opt.foreach(_ ! Sent (messageId))
137
- Behaviors .stopped
138
- }
139
- } else {
140
- policy match {
141
- case RelayChannelsOnly =>
142
- switchboard ! GetPeerInfo (context.messageAdapter(WrappedPeerInfo ), prevNodeId)
143
- waitForPreviousPeerForPolicyCheck(msg, nextNodeId)
144
- case RelayAll =>
145
- switchboard ! Peer .Connect (nextNodeId, None , context.messageAdapter(WrappedConnectionResult ).toClassic, isPersistent = false )
146
- waitForConnection(msg)
147
- }
116
+ private def withNextNodeId (msg : OnionMessage , nextNodeId : EncodedNodeId .WithPublicKey ): Behavior [Command ] = {
117
+ nextNodeId match {
118
+ case EncodedNodeId .WithPublicKey .Plain (nodeId) if nodeId == nodeParams.nodeId =>
119
+ OnionMessages .process(nodeParams.privateKey, msg) match {
120
+ case OnionMessages .DropMessage (reason) =>
121
+ replyTo_opt.foreach(_ ! DroppedMessage (messageId, reason))
122
+ Behaviors .stopped
123
+ case OnionMessages .SendMessage (nextNode, nextMessage) =>
124
+ // We need to repeat the process until we identify the (real) next node, or find out that we're the recipient.
125
+ queryNextNodeId(nextMessage, nextNode)
126
+ case received : OnionMessages .ReceiveMessage =>
127
+ context.system.eventStream ! EventStream .Publish (received)
128
+ replyTo_opt.foreach(_ ! Sent (messageId))
129
+ Behaviors .stopped
130
+ }
131
+ case EncodedNodeId .WithPublicKey .Plain (nodeId) =>
132
+ policy match {
133
+ case RelayChannelsOnly =>
134
+ switchboard ! GetPeerInfo (context.messageAdapter(WrappedPeerInfo ), prevNodeId)
135
+ waitForPreviousPeerForPolicyCheck(msg, nodeId)
136
+ case RelayAll =>
137
+ switchboard ! Peer .Connect (nodeId, None , context.messageAdapter(WrappedConnectionResult ).toClassic, isPersistent = false )
138
+ waitForConnection(msg)
139
+ }
140
+ case EncodedNodeId .WithPublicKey .Wallet (nodeId) =>
141
+ context.log.info(" trying to wake up next peer to relay onion message (nodeId={})" , nodeId)
142
+ val notifier = context.spawnAnonymous(Behaviors .supervise(PeerReadyNotifier (nodeId, timeout_opt = Some (Left (nodeParams.wakeUpTimeout)))).onFailure(SupervisorStrategy .stop))
143
+ notifier ! PeerReadyNotifier .NotifyWhenPeerReady (context.messageAdapter(WrappedPeerReadyResult ))
144
+ waitForWalletNodeUp(msg)
148
145
}
149
146
}
150
147
@@ -180,4 +177,15 @@ private class MessageRelay(nodeParams: NodeParams,
180
177
Behaviors .stopped
181
178
}
182
179
}
180
+
181
+ private def waitForWalletNodeUp (msg : OnionMessage ): Behavior [Command ] = {
182
+ Behaviors .receiveMessagePartial {
183
+ case WrappedPeerReadyResult (r : PeerReadyNotifier .PeerReady ) =>
184
+ r.peer ! Peer .RelayOnionMessage (messageId, msg, replyTo_opt)
185
+ Behaviors .stopped
186
+ case WrappedPeerReadyResult (_ : PeerReadyNotifier .PeerUnavailable ) =>
187
+ replyTo_opt.foreach(_ ! Disconnected (messageId))
188
+ Behaviors .stopped
189
+ }
190
+ }
183
191
}
0 commit comments