By Marcin Mielniczuk, Łukasz Foniok
Ockham’s Razor proves to be a very useful problem-solving principle not only in philosophy but also in everyday life. From the developer’s perspective, you could apply it like this — simpler theories are preferable to more complex ones because they are more testable.
That also sheds a lot of light on our struggle with Devp2p. For a long while we believed that if we could just solve the current critical issue we’d be fine and it would all be over.
Finally it dawned on us that it wasn’t ever going to be the case and our version of Ockham’s Razor prevailed. So instead of Devp2p in Brass, we will see p2p2 — an extended version of our own protocol currently used in Golem. And that crucial decision helped jumpstart a lot of other development!
The next section is very technical so feel free to skip.
Why have we discontinued considering Devp2p for Brass?
When we started our work, we began by implementing the protocol using pydevp2p. Golem under the protocol very quickly replaced all our existing p2p stack, so we felt excited because the entire Kademlia implementation was nicely packaged and contained in a separate module.
Unfortunately, it turned out that further integration with Golem’s core became a real problem. Pydevp2p using gevent tried to create its own event loop, which was necessary for it to switch greenlets, while we used reactor from Twisted.
We tried to reconcile both technologies with the geventreactor, which even performed the task on the posix platforms (Linux, MacOS). Unfortunately this was getting buggy on Windows.
We saw some light at the end of the tunnel, because meanwhile we decided to pay off some technological debt and move our entire code base to Python3. This gave us the ability to use the asyncio technology built into Python3.
The idea was that we would pass the reference of the asyncio loop to the gevent hub. This idea worked but unfortunately we again hit a wall with Windows. For Windows to achieve efficient event loops, which allow you to handle thousands of notifications, you need to use I/O Completion Ports (IOCP). It was the same with asyncioreactor and the tulipcore library, which implemented gevent loops on asyncio.
Despite all this, we didn’t give up. Marek Franciszkiewicz, our senior developer took up the challenge of removing our Twisted code, so we could resign asyncioreactor, but the tulipcore remained, and it stubbornly used SelectorEventLoop.
Eventually, we were able to create something that could play the role of the IOCP event loop on Windows. But we still weren’t satisfied with the solution. Problems kept cropping up. We’d hit a dead end, and it clearly didn’t make sense to roll over the entire code just to have it integrate with one module.
PoC bootstrap nodes
However paradoxical it may seem, every decentralized system needs at least a little centralisation. The reason is quite simple. Whenever you want to join the network, you must know at least someone who already is in the network. This is the purpose of bootstrap nodes — well-known nodes to allow a seamless jump into the network.
Currently, there is no practical distinction between a boostrap node and a full node in Golem. In other words, every bootstrap node runs just the same software as any other user of the network, with only a slightly different configuration.
When we have more specialised nodes that are optimized to be just bootstrap nodes and nothing more, we will be able to achieve much better network scalability. Those specialised nodes should only implement Kademlia and the simplest handshake that passes information about other nodes in the network. After they have passed the information to the new node, they should disconnect.
We’ve decided to prepare PoC of those simplified nodes in Golang, to evaluate whether we needed replace some modules in Golem’s app that are written in Python with Go implementations.
We progressed pretty far with our PoC and we certainly want to finish it and evaluate whether or not Go is a good fit for Golem.
Sybil attack protection
Whenever a computer system allows users to interact with each other, there is always the risk of that system facing what is known as a Sybil attack. The name gives little away in terms of description but it is a fairly basic type of attack to carry out, unless that system prepares accordingly.
You have probably solved hundreds of CAPTCHAs in your life. This how many mainstream services try mitigate against the threat of a Sybil attack; a single party creating so many identities that they are able to take over the network.
An attack’s form largely depends on the targeted system. If, for example, the victim is a social network, multiple accounts with a popular name and surname can be spawned. Later on, they can be used to malform the search results or spam legitimate users. In the case of the Golem network, the threat is of a different kind — malicious users can offer to carry out any computing task but never actually calculate anything — in other words, an attacker could crash the utility of the network by conducting a Denial-of-Service attack.
So why doesn’t Golem ask their users to solve CAPTCHAs? Wouldn’t that be an easy fix? The problem with such protection is twofold. Firstly, it means that a central server needs to maintain the CAPTCHA database and validate user inputs. That doesn’t sit well with Golem’s values and our founding idea of being a decentralized service. Moreover, CAPTCHA protection can be easily broken — there are services based in the developing world, offering CAPTCHA solving for a fraction of a cent!
Golem uses cryptography for authentication, namely — elliptic keys. Every key has a so-called fingerprint — a string, which is used to identify a cryptographic key. By design, it is difficult to create two keys with colliding fingerprints.
The whole of cryptography is based on randomness and as a consequence, the authentication keys are random too (or, more precisely, they look very random).
This means the fingerprint is a random string as well. As developers whenever we encounter a random variable, we know that we have something we can possibly use to create a Proof-of-Work system.
Now, let’s suppose we demand the public key fingerprint, treated as a number (you can easily achieve this by mapping the characters to numbers and interpreting the string as a positional notation) to be small enough. This means that only a fraction of generated keys will be accepted and since everything is probabilistic, we’ll need to repeat the whole process until we succeed.
That’s exactly what the Golem team did. By demanding that key generation be difficult enough, the generation of a single key takes a short while, just a couple of seconds. That imposes a cost on every attacker willing to create thousands of nodes — they will need hours or days to generate numerous valid identities. Of course, that doesn’t completely mitigate against a Sybil attack and additional measures will be added in the future. But it makes the chance of one occurring pretty remote.
Some of us from the dev team will meet next week with our colleagues from other projects during Devcon3. It will certainly be a great opportunity to exchange experiences and news. We will be happy to share our impressions with you in the next Dev Tech News.