--- Day changed Thu Sep 03 2015 00:18 -!- richardkiss [~richardki@70-36-142-78.dsl.dynamic.fusionbroadband.com] has joined #lightning-dev 00:27 -!- rusty [~rusty@pdpc/supporter/bronze/rusty] has left #lightning-dev [] 00:52 -!- go1111111 [~go1111111@104.200.154.68] has quit [Quit: Leaving] 00:58 -!- go1111111 [~go1111111@162.244.138.37] has joined #lightning-dev 01:18 -!- go1111111 [~go1111111@162.244.138.37] has quit [Ping timeout: 240 seconds] 01:19 -!- richardkiss [~richardki@70-36-142-78.dsl.dynamic.fusionbroadband.com] has quit [Quit: richardkiss] 01:20 -!- go1111111 [~go1111111@162.244.138.37] has joined #lightning-dev 01:21 -!- go1111111 [~go1111111@162.244.138.37] has quit [Excess Flood] 01:22 -!- go1111111 [~go1111111@162.244.138.37] has joined #lightning-dev 01:25 -!- Madars [~null@unaffiliated/madars] has quit [Ping timeout: 244 seconds] 01:26 -!- Madars [~null@unaffiliated/madars] has joined #lightning-dev 02:20 -!- bedeho [~bedeho@50-202-37-133-static.hfc.comcastbusiness.net] has quit [Ping timeout: 252 seconds] 03:50 -!- go1111111 [~go1111111@162.244.138.37] has quit [Ping timeout: 264 seconds] 03:51 -!- go1111111 [~go1111111@104.200.154.15] has joined #lightning-dev 03:54 -!- CodeShark [~CodeShark@cpe-76-167-237-202.san.res.rr.com] has quit [Ping timeout: 260 seconds] 05:51 -!- zzz [cc1c7c52@gateway/web/cgi-irc/kiwiirc.com/ip.204.28.124.82] has joined #lightning-dev 05:51 -!- zzz [cc1c7c52@gateway/web/cgi-irc/kiwiirc.com/ip.204.28.124.82] has quit [Client Quit] 07:46 -!- StephenM347 [~stephenm3@static-64-223-246-218.port.east.myfairpoint.net] has joined #lightning-dev 08:27 -!- bedeho [~bedeho@50-202-37-133-static.hfc.comcastbusiness.net] has joined #lightning-dev 08:38 -!- richardkiss [~richardki@70-36-142-78.dsl.dynamic.fusionbroadband.com] has joined #lightning-dev 09:03 -!- CJP [~CJP@a83-163-77-195.adsl.xs4all.nl] has joined #lightning-dev 09:19 -!- Guest82853 is now known as maaku 10:08 < CJP> mjerr, are you there? 10:08 < mjerr> yea 10:09 < CJP> Yesterday you wrote you wanted to take a deeper look into Amiko Pay, and I wonder if you're interested in a guided tour. :-) 10:09 < mjerr> sure! 10:09 < mjerr> =) 10:10 < mjerr> I'm not very keen in python though, but I guess with a helping hand... ;) 10:10 < CJP> OK, let's start at https://github.com/cornwarecjp/amiko-pay 10:11 < CJP> There's some legalese, a README and a doc directory; let's skip that for now. 10:11 -!- nullbyte [~NSA@193.138.219.233] has quit [Ping timeout: 244 seconds] 10:11 < mjerr> hehe, all the usual stuff 10:11 < mjerr> guess most of your recent work lies within prototype3 10:12 < CJP> python-prototype is the previous prototype; prototype3 is the new one. Let's dive into prototype3. 10:12 < mjerr> by the way, just as a side question, been thinking about this for a while.. is there any reason you don't make use of databases? it sounded yesterday as if you are saving the status quo in files? 10:13 < CJP> Yes, all recent work is in prototype3. I intend to remove python-prototype in the future. 10:13 -!- nullbyte [NSA@gateway/vpn/mullvad/x-mpdkurhgumpbhnfe] has joined #lightning-dev 10:15 < CJP> Not working with databases feels more convenient to me. 10:15 < CJP> If I wanted to use a database, I'd prefer a kind of database that woudn't need extra installation / configuration by a user. 10:15 < mjerr> Ah, so just personal preference. sqlite is great for that, if you ever choose to go with one, you can just deploy it together 10:16 < CJP> Consistency of data is important. With things like SQL you can define atomic transactions, which is great for consistency, but it means you'd need to express all the different complicated updates as SQL commands, which is difficult. 10:17 < mjerr> yea, it's a very powerful tool, especially for financial bookkeeping 10:18 < CJP> Now it's just a matter of updating some local variables, and if your code has no bugs and no errors occurred on the way, all local variables together represent a consistent state. 10:19 < CJP> The consequence is that I'm always overwriting the entire state. This is probably not optimal performance-wise, but it's OK for a prototype and even early releases. The state isn't that big anyway. 10:19 < mjerr> yea probably right, as long as you don't run into concurrency problems that should work 10:20 < mjerr> yea performance should really be the last thing to address 10:20 < mjerr> otherwise you end up with things like nosql... 10:21 < mjerr> so give me some basic overview of the architecture.. I guess you are setting up a server interface and listening to incoming connections? how do you handle those? 10:21 < CJP> Concurrency is solved the most simple way: an entire node is run by a single thread. It receives events from the network and the user interface, updates+stores its state accordingly and sends out network messages. 10:24 < CJP> In the prototype3 directory, you see main.py: that is just a small commandline wrapper around an amiko.node object. All real functionality is provided by amiko.node, so it can easily be integrated in larger applications and applications with different user interfaces (GUI, web etc.). 10:25 < mjerr> ah, a = node.Node() 10:25 < mjerr> ^^ 10:25 < CJP> Yes, that one. 10:26 < CJP> A node object creates its own thread, which can be start()ed and stop()ped. So, if you create multiple node objects, each has its own thread. 10:26 < mjerr> yea, just saw that, neat! 10:27 < CJP> This is great for testin scenarios with multiple nodes. :-) 10:28 < mjerr> yea, defo. just had to rewrite a lot of statics, as I ran into this exact problem ^^ 10:29 < CJP> So, entering amiko/node.py: 10:29 < mjerr> self.makeConnection(ID) - how do you establish connections? do you store IP addresses of other nodes? 10:31 < CJP> For each link to another node, a node stores info on how to connect to it. Typically, this is a host name (could be IP address), port number and the name of the link on the other side. 10:32 < mjerr> ah I see - do you make use of any identification methods, like saving a pubkey and validating a signature? 10:33 < CJP> I don't have any authentication yet and I don't have any encryption yet, so it's still very insecure. 10:34 < CJP> When establishing a link for the first time, users follow this procedure: 10:36 < CJP> Alice gives the command "makelink LinkToBob" (LinkToBob is the name of the link on Alice's side). This returns amikolink://host:port/LinkToBob. 10:36 < CJP> Then, Bob gives the command "makelink LinkToAlice amikolink://host:port/LinkToBob". 10:37 < CJP> Now, Bob's node knows how to connect to Alice, and immediately does so. Bob's node passes "call-back" connect information to Alice's node, so they can connect in both directions in the future. 10:39 < CJP> The line you asked about (self.makeConnection(ID)), is that line 136 in node.py? 10:39 < mjerr> ah, so the command makelink LinkToBob just saves the link, such that they will connect when bob tries to makes a connection 10:40 < mjerr> yea 10:40 < mjerr> thats about the 'old' saved links I guess 10:40 < CJP> Yes. After makelink LinkToBob, Alice just has an "empty" link, which doesn't know yet how to connect. 10:41 < mjerr> yea, waiting to be filled by bob 10:42 < CJP> Line 136 is in the node constructor (__init__ is a constructor in Python), so it's called at start-up to restore network connections. 10:42 < CJP> You can see in makeConnection (line 213+) that connections are skipped if not enough information is available. 10:43 < mjerr> yea. how do you establish connections? what do you pass as arguments when the connection is established? 10:43 < mjerr> ah I see 10:43 < CJP> Line 223 10:45 < CJP> Note that ID is the LOCAL ID; the ID of the link at the other side is in the connect message. 10:45 < mjerr> how does connectMessage look like? 10:46 < CJP> For links, it's an instance of the Connect class in amiko/core/messages.py. 10:46 < CJP> Sorry, I mean ConnectLink in amiko/core/messages.py. 10:47 < CJP> It's a simple serializable object with a couple of attributes. 10:48 < CJP> ID is the remote link ID (the ID of the link on the receiving side). 10:49 < CJP> CallbackHost/Port/ID tell the remote node how to call back this node (as explained earlier). 10:49 < mjerr> brb 10:53 < mjerr> messages is just a lose collection of message classes (not one class per file as in java), right? why do you send over the id of the link on the receiving side? isn't this rather error prone? 10:53 < CJP> dice is a special attribute: it's set to a random value. In the case that two nodes simultaneously connect to each other, it's used to judge which of the two connections should be closed and which should be used (by comparing the dice values). 10:54 -!- CodeShark [~CodeShark@cpe-76-167-237-202.san.res.rr.com] has joined #lightning-dev 10:55 < CJP> What do you mean by "error prone"? Easy to abuse by attackers? Sure: in that respect, the code is not ready for real-world usage. The ID is similar in function to a user name, not a password. 10:57 < mjerr> ahhhhhhhhh thats an interesting idea, I was thinking about this issue as well, rusty used some kind of priority to make sure nodes don't end up in a lock, not sure if i like that 10:59 -!- richardkiss [~richardki@70-36-142-78.dsl.dynamic.fusionbroadband.com] has quit [Quit: richardkiss] 11:00 < CJP> Note that the dice value is only used for establishing connections, not for channel updates. But yeah, maybe it's also useful as a solution for channel updates. 11:01 < mjerr> yea, but also as in 'why would the one node need to save essential data only required by the other node' - in case of a data loss it makes reestablishing the link more difficult, doesn't it? 11:01 < mjerr> yea, on channel updates there should be some extended logic, who should go first 11:01 < mjerr> but just using the dice value might work out 11:02 < mjerr> although this might be abused aswell.. at least I think so, not sure 11:03 < CJP> True. Nobody forces you to use a truly random dice value; you could artificially set it to the highest or the lowest value (whichever benefits you). 11:04 < CJP> In the case of establishing connections, nobody has is interested in the outcome, as long as there IS an outcome, so it works there. It might not work for channel updates, where more things are at stake. 11:04 < mjerr> yea, I don't know how this would benefit you though.. If you don't want to accept data from the other peer, you can just refuse alltogether 11:05 < mjerr> as long as both peers get to submit the updates they want to submit, the order is not really important I guess 11:06 -!- richardkiss [~richardki@70-36-142-78.dsl.dynamic.fusionbroadband.com] has joined #lightning-dev 11:07 -!- CodeShark [~CodeShark@cpe-76-167-237-202.san.res.rr.com] has quit [Ping timeout: 268 seconds] 11:08 < CJP> Maybe. I can't think of a scenario right now where channel updates from opposite directions could conflict each other. 11:09 < mjerr> yea me neither, might give it a shot, it's easy to implement at lesat 11:10 < CJP> There's one thing you need to avoid though: 11:10 < CJP> Suppose your channel is in state X, and you have updates a (from Alice's side) and b (from Bob's side). 11:11 < CJP> If both try to do the update simultaneously, you could end up with Alice sending the update X+a and Bob sending X+b. What's really needed in the end is X+a+b. 11:12 < mjerr> yea, but X+a+b should only be accomplished by doing two separate updates.. this can be resolved quite easily though, as long as you keep track of the revocation hashes 11:13 < mjerr> as long as the both parties submits ALL revocation hashes other than the current one to the other party, everything is fine 11:13 < CJP> So, either you detect+fix such a collision, or you need to prevent it (e.g. with a locking mechanism). In the first case, a dice value could determine who's responsible for fixing. 11:16 < mjerr> do you have some revocation logic in place currently? 11:17 < CJP> I was trying to find what you mean with "revocation hash", but I couldn't find it immediately. Could you explain? 11:18 < mjerr> oh, sure! when you commit to a state, you are exchanging hashes that the other party has the preimage of. when you want to commit to a new state, you revoke the old state by exchanging the preimages of old states 11:20 < CJP> Ah. In Amiko Pay, that's left to the channel implementation. For several transaction phases, a channel can add a payload to Amiko Pay network messages. Channel implementations are free to choose how to use that. 11:21 < CJP> Note that there is no implementation yet of a Lightning-style transaction channel for Amiko Pay. 11:23 < mjerr> yea, forgot about that. So amiko pay is not even strictly bound to bitcoin either, right? it's rather the core of the messaging system necessary for systems like lightning 11:24 < CJP> True. In its most minimialistic form, it's more like Ryan Fugger's original design of Ripple. 11:25 < mjerr> so how does ap(amiko pay) handle the communications necessary for more complex systems? is it up to the channel implementation to say how many messages back and forth are necessary and handle those? 11:26 < CJP> In channels/plainchannel.py you can find the minimalistic "dummy" channel implementation. 11:27 < CJP> The system is not complete yet, but I have plans where channels can chat a bit back and forward if they need more than just a one-time payload in an Amiko message. 11:29 < CJP> This is already used for depositing/withdrawing, but not yet for transactions. 11:32 < mjerr> but I could always just use multiple amiko messages? the state will be saved after each message I guess, right? 11:34 < CJP> If a channel conversation involves multiple messages, those messages WILL be encapsulated in multiple Amiko messages. And yes, after each of them, the state will be saved. 11:35 -!- bedeho [~bedeho@50-202-37-133-static.hfc.comcastbusiness.net] has quit [Ping timeout: 264 seconds] 11:35 < CJP> Channel conversations are done by calling handleMessage on the channel object (see e.g. a channel implementation in channels/plainchannel.py). 11:37 < CJP> The input argument is a message received from the remote node (None at the start of a conversation). The return value is sent to the remote node, or it is None at the end of a conversation. 11:38 < CJP> This part of the code is still a bit under construction though: I need to figure out whether this also works well for other use cases of channel conversations. 11:39 < mjerr> yea, you would definitely need a way to define whether you expect another message afterwards 11:39 < CJP> What should Amiko Pay do with that info? 11:41 < mjerr> hm.. for some cases you would want to close the channel soonish if you don't get an answer back 11:42 < CJP> Ahh, so you want to receive a time-out event? I already guessed there would be a need for that. Yes, I still need to build infrastructure for delivering time-out events to channels. 11:44 < mjerr> where would you put in the blockchain-listening logic? 11:46 < CJP> In the previous prototype, I had a "watchdog", which kept a list of things to look out for. It periodically polled bitcoind for those things, and emitted events in case anything interesting occurred. 11:47 < CJP> I'll add it again in prototype3 when it's needed. I don't know yet how to let it interface with channel objects. Obviously, channel objects are probably the only things interested in block chain events. 11:48 < mjerr> yea, channel objects will have to add listener to the watchdog somehow 11:48 < mjerr> can't add a callback? 11:48 -!- bedeho [~bedeho@50-202-37-133-static.hfc.comcastbusiness.net] has joined #lightning-dev 11:49 < CJP> The architecture is still TBD, but I think a callback is problematic, since it's difficult to store a function pointer to disk in the node state file. 11:51 < CJP> In the previous prototype I would have used callbacks, but now I'll probably give the watchdog a message object which must be emitted in case of the event. The message object should contain the right information to find its way back to the channel object. 11:51 < mjerr> hm, I have no idea about python, but as you will end up with many different channel structures (there are a lot of interesting things you can do with such a general structure like amiko pay) 11:54 < CJP> as ..., 11:54 -!- nullbyte [NSA@gateway/vpn/mullvad/x-mpdkurhgumpbhnfe] has quit [Ping timeout: 255 seconds] 11:55 < mjerr> oh - those channels will want to enforce different things, depending on what they find on the blockchain 11:55 < mjerr> maybe they just want to save some data 11:55 -!- nullbyte [NSA@gateway/vpn/mullvad/x-fisjkgfcvwgzwyly] has joined #lightning-dev 11:59 < CJP> Sure. Channels can store data locally as part of their own state, or they will be able to publish transactions on the block chain. They could use other means as well (store their own files, make alternative network connections etc.), but that could quickly become unreliable / confusing. 12:01 < mjerr> ah perfect.. how does timing an operation work out? like 'publish this transaction in 4 days' 12:04 < CJP> That's a matter of creating a time-out message. Amiko Pay keeps a list of time-outs: whenever a time-out has passed, the corresponding message is emitted. This list of time-outs + messages is stored as part of the node state. 12:05 < mjerr> ah I see, so you have a service keeper for this already. neat! 12:05 < CJP> Of course, a node can only react on a time-out when it's running. If a node starts up and discovers that some time-outs have passed, it will immediately send out the time-out messages. 12:06 < mjerr> you want to make sure it is completely done with starting up, as you want to know if you still want to send these messages 12:06 < mjerr> but I guess you have done that already 12:07 < CJP> It's the responsibility of the receiver to check whether the time-out message still applies. 12:07 < mjerr> oh, I was thinking about blockchain transactions as well 12:07 -!- bedeho [~bedeho@50-202-37-133-static.hfc.comcastbusiness.net] has quit [Ping timeout: 255 seconds] 12:08 < CJP> What do you mean? 12:08 < mjerr> not just timing messages to other nodes, but also transactions to broadcast 12:09 -!- nullbyte [NSA@gateway/vpn/mullvad/x-fisjkgfcvwgzwyly] has quit [Ping timeout: 265 seconds] 12:10 < CJP> Oh, i see. Some explanation: Amiko Pay uses message objects for a lot of internal communication as well as communication to peers. So, the primary way to use the time-out service is to let it emit a message that will be received by some internal object. 12:11 -!- nullbyte [NSA@gateway/vpn/mullvad/x-dndfbsndwoczsyvx] has joined #lightning-dev 12:12 < mjerr> ah I see. So lets say there is a transaction on schedule to be transmitted in one hour.. then your node goes down and powers up after two hours again. In the meantime however things changed on the blockchain and you really don't want to send that transaction anymore. Thats what I meant about everything starting up first 12:14 < CJP> Maybe your channel could, upon receiving the time-out on start-up, do something like: "oh, the block chain is not synced yet. Now I want to receive a message when the block chain has synced." 12:15 < CJP> Then, once the block chain has synced, the channel queries the block chain and determines the transaction shouldn't be sent anymore. 12:15 < mjerr> yea, you should really keep this in mind. Not just mindlessly sending all messages because they were scheduled this way 12:17 < CJP> True, but the time-out service can't know about all these considerations, so the best it can do is to always do its job and send out its message. The object requesting the time-out should know how (and when) to act on it. 12:18 < CJP> when = under what conditions 12:18 < mjerr> it should be possible to give them IDs and have the channel implementations check the IDs back again before the timeout-service starts 12:19 < mjerr> quite complicated though 12:19 < mjerr> maybe put it in some TODO somewhere 12:19 < CJP> You mean, so that no-longer-applicable events can be removed before they are emitted? 12:19 < mjerr> yea exactly 12:20 < CJP> That might be useful, but not strictly necessary for that use case. There is another use case though: 12:20 < CJP> The case that the object that's supposed to receive the message is deleted. 12:21 < mjerr> that should end up with some random error though, with no real consequences? 12:21 < CJP> True. Worse: another object with the same ID is created after deleting the old object, so the new object is at risk of receiving the message and being confused by it. 12:22 < mjerr> THAT however is a horrible outcome 12:22 < mjerr> and will produce the worst errors 12:22 -!- bedeho [~bedeho@50-202-37-133-static.hfc.comcastbusiness.net] has joined #lightning-dev 12:22 < mjerr> some way to ensure strictly upcounting IDs? 12:24 < CJP> I may need to reconsider the whole way objects are given IDs and how messages are delivered. Currently, there's too many places where ID uniqueness is implicitly assumed, while it's not guaranteed. 12:25 -!- nullbyte [NSA@gateway/vpn/mullvad/x-dndfbsndwoczsyvx] has quit [Ping timeout: 260 seconds] 12:25 < CJP> Making object IDs truly unique will make a lot of things more reliable and probably even more simple. 12:25 < mjerr> yea defo 12:26 < mjerr> could use GUID 12:26 -!- nullbyte [NSA@gateway/vpn/mullvad/x-ejxokjabtjbckkox] has joined #lightning-dev 12:31 < CJP> That's one option (or actually several options, since there's several GUID schemes). 12:33 < CJP> Anyway, I may need to step away from using user-supplied IDs (such as link names) as destination for messages, at least, for internal messaging. 12:36 < CJP> For external messaging, maybe the connect message can include the link name for identification. It should not be necessary to transmit IDs over the network; the receiving network interface can fill in the link ID, based on the link name, for internal delivery of the message. 12:39 < mjerr> or you include some pubkey sceme and use that 12:41 < CJP> For internal messaging, that is overkill. For external messaging, the advantage of a pubkey scheme is it can be combined with encryption + digital signing. That's for instance how bitcoin addresses are used. The disadvantage is they're not human readable. 12:42 < mjerr> hm.. some hash of it.. yea its difficult. A database on the other hand.. :P 12:44 < CJP> So, I'd only use a "pubkey scheme" if it actually offers advantages in terms of being tied to a cryptographic identity for encryption / signing. 13:03 -!- richardkiss [~richardki@70-36-142-78.dsl.dynamic.fusionbroadband.com] has quit [Quit: richardkiss] 13:05 -!- richardkiss [~richardki@70-36-142-78.dsl.dynamic.fusionbroadband.com] has joined #lightning-dev 13:17 < CJP> @mjerr: I guess it's time to end the "guided tour" to Amiko Pay for today, unless you have any questions now. Thanks for the discussion: it gave me some new ideas on how to improve the design. 13:28 -!- CJP [~CJP@a83-163-77-195.adsl.xs4all.nl] has quit [Quit: Ik ga weg] 13:33 -!- CodeShark [~CodeShark@cpe-76-167-237-202.san.res.rr.com] has joined #lightning-dev 13:42 -!- mjerr [~mjerr@p5B209723.dip0.t-ipconnect.de] has quit [Ping timeout: 240 seconds] 13:55 -!- mjerr [~mjerr@p5B209723.dip0.t-ipconnect.de] has joined #lightning-dev 14:55 -!- StephenM347 [~stephenm3@static-64-223-246-218.port.east.myfairpoint.net] has quit [] 15:00 -!- mjerr [~mjerr@p5B209723.dip0.t-ipconnect.de] has quit [Ping timeout: 264 seconds] 16:33 -!- bedeho [~bedeho@50-202-37-133-static.hfc.comcastbusiness.net] has quit [Ping timeout: 250 seconds] 17:23 -!- bedeho [~bedeho@50-202-37-133-static.hfc.comcastbusiness.net] has joined #lightning-dev 17:26 < kanzure> writeup about the cross-chain multi-chain nodes for payment channel networks (from the other day in here): http://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-September/010909.html 17:30 -!- rusty [~rusty@ip9.cor1.adl1.base64.com.au] has joined #lightning-dev 17:30 -!- rusty [~rusty@ip9.cor1.adl1.base64.com.au] has quit [Changing host] 17:30 -!- rusty [~rusty@pdpc/supporter/bronze/rusty] has joined #lightning-dev 18:26 -!- CodeShark [~CodeShark@cpe-76-167-237-202.san.res.rr.com] has quit [Ping timeout: 256 seconds] 20:14 -!- richardkiss [~richardki@70-36-142-78.dsl.dynamic.fusionbroadband.com] has quit [Quit: richardkiss] 20:30 -!- richardkiss [~richardki@70-36-142-78.dsl.dynamic.fusionbroadband.com] has joined #lightning-dev 22:30 -!- mjerr [~mjerr@p5B209723.dip0.t-ipconnect.de] has joined #lightning-dev 22:31 -!- nullbyte [NSA@gateway/vpn/mullvad/x-ejxokjabtjbckkox] has quit [Ping timeout: 250 seconds] 22:33 -!- nullbyte [NSA@gateway/vpn/mullvad/x-ltbtdakhauffhhfz] has joined #lightning-dev 22:38 -!- nullbyte [NSA@gateway/vpn/mullvad/x-ltbtdakhauffhhfz] has quit [Read error: Connection reset by peer] 22:42 -!- nullbyte [NSA@gateway/vpn/mullvad/x-osqhtogxmctquags] has joined #lightning-dev 23:04 -!- nullbyte [NSA@gateway/vpn/mullvad/x-osqhtogxmctquags] has quit [Ping timeout: 244 seconds] 23:04 -!- rusty [~rusty@pdpc/supporter/bronze/rusty] has left #lightning-dev [] 23:06 -!- nullbyte [NSA@gateway/vpn/mullvad/x-azpbogjcrdsegsbn] has joined #lightning-dev 23:08 -!- meep_ [18da6e6e@gateway/web/freenode/ip.24.218.110.110] has joined #lightning-dev 23:13 -!- nullbyte [NSA@gateway/vpn/mullvad/x-azpbogjcrdsegsbn] has quit [Ping timeout: 252 seconds] 23:15 -!- nullbyte [~NSA@193.138.219.233] has joined #lightning-dev 23:33 -!- trippysalmon [rob@2001:984:6466:0:51d:b5ab:ab61:bed8] has joined #lightning-dev 23:39 -!- trippysalmon [rob@2001:984:6466:0:51d:b5ab:ab61:bed8] has quit [Ping timeout: 250 seconds]