This is not a Blockchain-bullshit-only post. Head to https://github.com/ice09/onboarding-eip712 for a sample implementation of a private Testnet onboarding with Twitter, Reddit or Github Keybase proofs.
The challenge…
Creating a private Ethereum Proof-of-Authority testnet is actually quite easy, just follow the instructions of Parity or, for testing purposes only, cliquebait, and you’re done.
But how do you onboard new users, who want to participate in your testnet, if they don’t have any ETH to start with and you still want to stay as close to your production environment in terms of gas usage as possible and therefore don’t want to enable gasless transactions in your PoA testnet?
…and the public testnet solutions
Kovan PoA
The public (Parity-powered) Kovan PoA solves this problem by providing a “Faucet Service” which is verified by
- Github User Verification (OAuth)
- SMS Verification (developed by Parity)
- Manually by consortium members
Rinkeby PoA
The public (Geth-powered) Rinkeby PoA solves this problem by providing a “Faucet Service” which is verified by
- Github User Verification (OAuth)
- Facebook User Verification (OAuth)
- Twitter User Verification (OAuth)
However, these methods require trust or establish dependencies on the testnet providers, which you will and can most likely not introduce in your private net.
Analysing the problem
There are (at least) two different user states which a user can have in your PoA private testnet:
- The user has an authorized account in one of the authority node wallets.
- The user is connected to the private testnet, but initially has no balance.
In Proof-of-Work (PoW), the non-authorized user could start mining and get ETH for contract development. This is the Ropsten solution. In PoA, mining is not possible, so the user has no possibility to transact on the chain, except for using usd_per_tx=0 or similar for no gas costs on the authority nodes, which is possible, but contradicts the security measures established by having gas usage for transactions to minimize code execution, prevent infinite loops, etc. Even more, the setup on a test stage should always be similar to the production environment.
Recentralizing for “Almost Know Your Customer”
keybase.io does a great job in identifying users without revealing their real life identity if they don’t want to. But they make it quite difficult for attackers to create several identities. So a good enough solution for onboarding otherwise unknown users could be to bind their testnet accounts to their keybase users.
The basic idea and the following approach is inspired by Aragon and one of their great blog posts, which every Ethereum/Solidity dev should take a look at.
In our setup, we changed two crucial factors:
- removed dependencies on a product (Aragon) and on the Oracle (oraclize.it) and
- introduced a middleware component as a new dependency, but which you control and can (and have to) host on your own server.
keybase allows for publishing files per HTTPS by copying them to KBFS file system. Thereby, someone reading a file from https://KEYBASE_USER.keybase.pub/invite_me.json can be sure that the file invite_me.json was stored by keybase KEYBASE_USER.
But this is just one proof, how can we be sure that the user really has the private key for the address he wants to be registered? We can just sign the message with the private key and ecrecover in a contract.
Using this mechanism, we can be sure that:
- If the URL https://KEYBASE_USER.keybase.pub/invite_me.json gives back the correct JSON, the user is who he pretends to be, since he had to copy invite_me.json to KBFS
- The address (private key) belongs to the user, since he signed the message in invite_me.json and the signature is ecrecovered in the invite_me contract.
MetaMask does a great job in helping signing data with your Ethereum’s account private key. However, up to EIP-712 the signed data has been displayed as a non-human-readable Hex string.
This is unacceptable, especially in crypto, a field of very easy and unavoidable fraud attempts.
Therefore, we are supporting EIP-712 by signing with a MetaMask version supporting the new eth_signTypedData method. EIP-712 is a major step forward and should be the only way users are required to sign data with their Ethereum account’s private key.
The following overview shows the process which let a new user register his keybase user to a new generated address. Afterwards, 10 ETH are sent to this address. The user can only register once. However, the contract owner can manually unregister users if necessary.
For the ETH transfer to happen, the user must
- have the invite_me.json as generated by the frontend part stored in his KBFS public directory, so that it can be retrieved by the server side process at the keybase.pub domain
- have at least one of these Keybase proofs: Twitter OR Github OR Reddit
Introducing invite_me
The complete process is implemented in the “DApp” invite_me, which consists of a JavaScript frontend and a web3j-powered Java backend component called verifier.
After completing the steps, you can see the 10 ETH loaded into your account in MetaMask.
Let me try this!
First, checkout this. Then, make sure that you have an understanding how the authorization and verification works and what the Java component verifier does and how they depend on each other.
Last, install it locally and try it out. And, most importantly, please comment here or in the reddit post if it doesn’t work for you or if you have remarks about the approach, obviously this is work in progress.
What comes next?
We’d like to try two different approaches:
- Using 3box as a more decentralized (but not as mature) keybase alternative
- Realizing a different use case: “Almost KYC Airdrops”