Skip to content

Commit b3bd795

Browse files
committed
Clean up e2e test, move docs to Joinmarket-Docs
e2e-coinjoin-test.py now allows configuration of multiple directory nodes as well as multiple non-directory nodes. Also some irrelevant details were removed or cleaned up. The documentation of the lnonion messaging protocol was removed from jmdaemon/lnonion.py and relocated to JoinMarket-Org/JoinMarket-Docs#9
1 parent 1c87adf commit b3bd795

3 files changed

Lines changed: 66 additions & 234 deletions

File tree

jmdaemon/jmdaemon/lnonion.py

Lines changed: 10 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
from twisted.protocols.basic import LineReceiver
1111
log = get_log()
1212

13+
"""
14+
Messaging protocol (which wraps the underlying Joinmarket
15+
messaging protocol) used here is documented in:
16+
Joinmarket-Docs/lightning-messaging.md
17+
"""
18+
1319
LOCAL_CONTROL_MESSAGE_TYPES = {"connect": 785, "disconnect": 787, "connect-in": 797}
1420
CONTROL_MESSAGE_TYPES = {"peerlist": 789, "getpeerlist": 791,
1521
"handshake": 793, "dn-handshake": 795}
@@ -35,193 +41,14 @@
3541
"proto-ver-max": JM_VERSION,
3642
"features": {},
3743
"accepted": False,
38-
"nick": ""
44+
"nick": "",
45+
"motd": "Default MOTD, replace with information for the directory."
3946
}
4047

4148
# states that keep track of relationship to a peer
4249
PEER_STATUS_UNCONNECTED, PEER_STATUS_CONNECTED, PEER_STATUS_HANDSHAKED, \
4350
PEER_STATUS_DISCONNECTED = range(4)
4451

45-
"""
46-
### MESSAGE FORMAT USED on the LN-ONION CHANNELS
47-
48-
( || means concatenation for strings, here)
49-
50-
Messages conveyed between directly connected nodes with `sendcustommsg`
51-
have the format described here:
52-
53-
https://lightning.readthedocs.io/PLUGINS.html#custommsg
54-
55-
Note in particular, that `type` is a two byte hex-encoded string
56-
that prepends the actual message (also hex-encoded), without any intervening
57-
length field. This `type` is the integer specified in this file as *_MESSAGE_TYPES.
58-
59-
Note also that the type *must* be odd for custom messages (the "it's OK to be odd" principle).
60-
61-
In text, the messages we send via this mechanism are of this format:
62-
63-
from-nick || COMMAND_PREFIX || to-nick || COMMAND_PREFIX || cmd || " " || innermessage
64-
65-
(COMMAND_PREFIX defined in this package's protocol.py)
66-
67-
Here `innermessage` may be a list of messages (e.g. the multiple offer case) separated by COMMAND PREFIX.
68-
69-
Note that this syntax will still be as was described here:
70-
71-
https://github.com/JoinMarket-Org/JoinMarket-Docs/blob/master/Joinmarket-messaging-protocol.md#joinmarket-messaging-protocol
72-
73-
Note also that there is no chunking requirement applied here, based on the assumption that we have a sufficient 1300 byte limit.
74-
That'll probably be changed later.
75-
76-
### CONTROL MESSAGES
77-
78-
#### HANDSHAKE CONTROL MESSAGES
79-
80-
The message `handshake` is sent by any peer/node not configured to act as
81-
directory node, to any other node it connects to, as the first message.
82-
The message `dn-handshake` is sent by any peer/node which is configured to
83-
act as a directory node, to any other node, as a response to the initial
84-
`handshake` message.
85-
(Notice that this configuration implies that directory nodes do not currently
86-
talk to each other).
87-
88-
The syntax of `handshake` is:
89-
json serialized:
90-
{"app-name": "joinmarket",
91-
"directory": false,
92-
"location-string": "hex-key@host:port",
93-
"proto-ver": 5,
94-
"features": {},
95-
"nick": "J5***"
96-
}
97-
Note that `proto-ver` is the version specified as `JM_VERSION` in jmdaemon.protocol.
98-
(It has not changed for many years, it only specifies the syntax of the messages).
99-
The `features` field is currently empty.
100-
101-
The syntax of `dn-handshake` is:
102-
json serialized:
103-
{"app-name": "joinmarket",
104-
"directory": true,
105-
"proto-ver-min": 5,
106-
"proto-ver-max": 5,
107-
"features": {}
108-
"accepted": true,
109-
"nick": "J5**"
110-
}
111-
112-
Non-directory nodes should send `handshake` to directory nodes,
113-
and directory nodes should return the `dn-handshake` method with `true`
114-
for accepted, if and only if:
115-
* the protocol version is in the accepted range
116-
* the `directory` field of the peer is false
117-
* the `app-name` is joinmarket
118-
* the set of features requested is both recognized and accepted (currently: none)
119-
* the nick used by this entity/bot across all message channels, used for cross-channel message spoofing protection
120-
121-
Notice that more than one nick is NOT allowed per LN node; this is deferred to
122-
future updates.
123-
124-
In case those conditions are met, return `"accepted": true`, else return
125-
`"accepted": false` and immediately disconnect the new peer.
126-
(in this rejection case, the remaining fields of the `dn-handshake` message do
127-
not matter, but can be kept as before for convenience).
128-
129-
In case of a direct connection between peers (neither are directory nodes),
130-
the party which connects then sends the first `handshake` message, and the
131-
connected-to party responds with their own `handshake`.
132-
133-
In this case, the connection should be accepted and maintained by the receiver
134-
if and only if:
135-
* the protocol version is identical
136-
* the `directory` field of the peer is false
137-
* the `app-name` is joinmarket
138-
* the set of features is both recognized and accepted (currently: none)
139-
140-
otherwise the peer should be immediately disconnected.
141-
142-
ALL OTHER MESSAGES (control or otherwise, as detailed below), cannot be sent/
143-
will be ignored until the above two-way handshake is complete.
144-
145-
#### OTHER CONTROL MESSAGES
146-
147-
The syntax of `peerlist` is:
148-
149-
nick || NICK_PEERLOCATOR_SEPARATOR || peer-location || "," ... (repeated)
150-
151-
i.e. a serialized list of two-element tuples, each of which is a Joinmarket nick
152-
followed by a peer location.
153-
154-
`peerlist` may be sent by directory nodes to non-directory nodes at any time,
155-
but currently it is sent according to a specific rule described below.
156-
157-
#### LOCAL CONTROL MESSAGES
158-
159-
There are two messages passed inside the plugin, to Joinmarketd, in response to events in lightningd,
160-
namely the `connect` and `disconnect` events triggered at the LN level. These are used to update
161-
the *state* of existing peers that we have recorded as connected at some point, to ourselves.
162-
163-
The mechanisms here are loosely synchronizing a database of JM peers, with obviously the directory
164-
node(s) acting as the data provider. It's notable that there are no guarantees of accuracy or
165-
synchrony here.
166-
167-
### PARSING OF RECEIVED JM_MESSAGES
168-
169-
170-
The text will be utf-encoded before being hexlified, and therefore the following actions are needed of the receiver:
171-
172-
Extract the peerid of the sender, and the actual message, received as encoded json:
173-
174-
* peerid: json.loads(msg.decode("utf-8"))["peer_id"]
175-
* message: json.loads(msg.decode("utf-8"))["payload"]
176-
(which is prepended by a two byte message_type; see below).
177-
178-
##### peerid:
179-
180-
This field comes in three potential forms (as hex):
181-
"00" : special null peerid indicating a local control message (see above).
182-
"hex-key": peerid without connection information, allowing us to record the existence of a peer,
183-
but not to send messages to it directly (only via the directory node).
184-
"hex-key@host:port": peerid with connection information, allowing us to attempt to connect to it,
185-
and send private messages to it directly.
186-
187-
##### payload:
188-
189-
This is parsed by:
190-
191-
1. Take the first two hex-encoded bytes and convert to an integer: this is the
192-
message type. Take the remaining part of the hex string and unhexlify,
193-
converting to binary, then .decode("utf-8") again, converting to a string.
194-
This means encoding is sometimes very inefficient, if the underlying string actually
195-
contains hex or other encoding, but can't really be changed until the underlying
196-
Joinmarket messaging protocol is changed.
197-
198-
2. Split the decoded string by COMMAND PREFIX and parse as `from nick, to nick, command, message(s)`
199-
(see above for syntax).
200-
201-
The resulting messages can be passed into Joinmarket's normal message channel processing, and should be
202-
identical to that coming from IRC.
203-
204-
However, before doing so, we need to identify "public messages" versus private, which does not
205-
have as natural a meaning here as it does on IRC; we impose it by using a to-nick value of PUBLIC
206-
and by sending the message_type `687` to the Lightning RPC instead of the default
207-
message_type `685` for privmsgs to a single counterparty. This will instruct the directory node
208-
to send the message to every peer it knows about.
209-
210-
### GETTING INFORMATION ABOUT PEERS FOR DIRECT CONNECTIONS
211-
212-
To avoid passing huge lists of peers around, the directory node takes a "lazy" approach to
213-
sharing connection info between peers:
214-
215-
When Peer J51 asks to privmsg J52 (which it discovered when receiving a privmsg from J52, usually
216-
here that would be in response to a `!orderbook` pubmsg by J51), the directory node does as instructed,
217-
but then sends also a `peerlist` message to J51, containing the full network location of J52.
218-
219-
Given this new information, J51 opportunistically tries to connect to J52 directly and if the network
220-
connection succeeds, sends a handshake to J52. If J52 responds with acceptance, the direct messaging
221-
connection is established, and from then on, until J51 sees a disconnect event for that network peer,
222-
he will divert any `privmsg` to that party to use the direct connection instead of the directory node.
223-
224-
"""
22552

22653
""" this passthrough protocol allows
22754
the joinmarket daemon to receive messages
@@ -826,6 +653,8 @@ def forward_privmsg_to_peer(self, nick, message, from_nick):
826653
assert self.self_as_peer.directory
827654
peerid = self.get_peerid_by_nick(nick)
828655
if not peerid:
656+
log.debug("We were asked to send a message from {} to {}, "
657+
"but {} is not connected.".format(from_nick, nick, nick))
829658
return
830659
# The `message` passed in has format COMMAND_PREFIX||command||" "||msg
831660
# we need to parse out cmd, message for sending.

0 commit comments

Comments
 (0)