We started working on Universal Ids suggested by Alex Van de Sande. We strayed of the smart contract standard and changed the guidelines to optimise for UX. Also created a full p2p second layer network of relayer nodes. Made the network Sybil attack proof by instantiating contracts counterfactually — upon meaningful action.
How it all began — Universal Identities
You can read how we implemented the universal identity smart contracts in Part One.
All is good in the smart contract world, but how does one send transactions to this smart contract if you do not posses ETH in your wallet. This is where the Relayer comes into play.
Paying for your users transactions
The relayer is a software node, an API, that allows you to submit your signature of your intent through it. In its simplest form it just takes your signature and sends it to the smart contract covering for the costs.
Here is the simplest implementation:
A lot of Dapps trying to get adoption, given a suitable business model, can implement this Relayer architecture and essentially pay for the transactions of their users. Even better, this maintains the traceability and immutability promise of the blockchain.
Although a very cool solution, it’s very hard to find a dapp business model that can afford to pay for their users transactions. This essentially means that the relayer should be reimbursed. This reimbursement can happen trustlessly with ETH or tokens. The general idea is for somebody (either the user or the dapp) to deposit ETH/tokens in the identity smart contract. These funds will be used as reimbursement for the relayer and the reimbursement logic will be coded up into the smart contract. The user will be able to quote a reward for the relayer. If the relayer deems this reward enough, he will execute the transaction and get reimbursed. Here is the exemplary contract code. Much of it you’ve already seen.
In order to stop the relayer from overriding the reward, you need to embed it into the request by including it in the data that is later signed. Such a relayer approach, essentially means that you can make a profit being relayer, thus making this a viable business model for outside parties to employ. Instead of you using your own ETH to relay, you can outsource this investment to outside relayer service and allow them to profit on it.
Another important point to discuss is the implication of having such a relayer in terms of architecture and trustlessness. Basically, such a relayer is a central point. As such, all the downsides of the centralization are assumed on this architecture too — single point of failure and censorship.
In order to fight this problem, we need to decentralize the relayer — create a network of relayers. You can read more about making a network of relayers in the upcoming Part Three. Before we enter the realm of p2p networks, we have one last important aspect to consider — how does one even obtain an identity contract?
Getting Identity Contract and Securing the Relayer
Our initial assumption is that the user does not have any ETH — it is only later when they need to execute a transaction that they deposit some funds (ETH or tokens) in order for the relayer be reimbursed. If the user does not have any ETH how does the user interact with the blockchain to deploy their identity contract?
Deploying the identity contract
In order for ether-less user to obtain identity contract, one must again request help from the only party in our network with a constant supply of ETH that is willing to spend it right away — the relayer. Essentially, we’ve do augment the relayer with another function — creation of Identity Contract. The process of creation of the Identity contract is as follows:
- The user signs a message that is passed to the constructor of the newly created smart contract.
- The smart contract, using the magical
ecrecoverfinds out who has signed the message and sets it as its master.
Once the relayer is done performing its job, he returns an address back to the user — the address of their newly created Identity Proxy. Yey. This approach, however, means very high risk-low reward situation for the relayer as there is no reimbursement of the relayer.
Reimbursing the relayer
Would you want to be a relayer if you know that you have a very high chance of losing your ETH and very low chance of getting just a little bit more? I wouldn’t. We need relayers though, therefore we must make the system to behave in such a way that the relayer has a very low risk of losing money and a high chance of profiting.
This means that the relayer should be reimbursed for deploying the user Identity Contract. One initial approach can be to reimburse the relayer for the deployment when the first transaction of the user is executed. Although this might work in some cases this is still high-risk situation for the relayer because:
- The user might not make a single transaction.
- If we have a network of relayers, the relayer node that has deployed the contract might be different from the one executing the first transaction. The wrong relayer will get reimbursed.
This essentially means that the Relayer is open for Sybil attacks. A malicious user can ask the Relayer to create for them an infinite amount of contracts, thus draining the relayer of their ETH. The only way to make this situation secure for the relayer is to ask the user to deposit funds in the address that their Identity Contract will be deployed, before actually deploying it.
Counterfactual instantiation of Identity Contracts
The pattern of getting the address of a contract before actually deploying it is made more popular by L4 in their Counterfactual State Channels paper.
In their paper, the L4 team creates a transaction that once submitted is going to deploy a contract. This transaction is not broadcasted but instead kept until needed. This way you do not need to spend ETH for setting up this contract before you actually need it.
A somewhat similar approach is what we took — Counterfactual instantiation of the identity contracts. When the relayer is asked to create an identity contract — he just creates a deployment transaction, sees the would-be address and returns it to the user without broadcasting the transaction.
Once the user decides to execute an action through their identity, the relayer checks the deposited amount and deploys the contract only if it is reimbursed correctly and sufficiently.
Although this approach seems quite easy to pull off in theory, the reality of finding the address of the would-be deployed transaction can prove troublesome. The address of the deployed contract is determined by hashing the address of the wallet of the deployer and the nonce of the wallet of the deployer. This makes it very easy to know the next addresses by incrementing the nonce of the relayer wallet.
The problem comes when you have to counterfactually instantiate multiple contracts (for multiple users) and you have to do the real deployment of, let’s say the last user. Keep in mind that you’ve asked all these users to eventually put funds in the addresses you’ve returned to them. If the last user is the first one that decides to use their counterfactual identity, in reality, the relayer would need to deploy all the previously “promised” contracts in order to increase their nonce sufficiently.
This basically means that the relayer is in the same high-risk-low-reward situation as they would need to assume the costs for multiple deploys of identity contracts. Here is where magic comes into play.
Deployment wallet per user
In order to fight the upper problem, one needs to have the certainty of the deployer wallet address and the nonce of this address without any relationship with the history of deployments. One way to achieve this is for the relayer to create a new deployment wallet for every user. This guarantees that the wallet address and nonce (being 0) is known at all times. Once the relayer wants to deploy this contract, he funds the deployment wallet and then broadcasts the counterfactual transaction.
Although this approach is good enough when we are talking about single relayer node, a problem arises when we start talking about a network of relayer nodes. Suddenly you, as a relayer, are required to fund deployment wallets that are created by other relayers. Malicious relayer might listen on these wallets and quickly drain the deployment ETH you’ve sent (via sending high gas price transaction). More on that in Part There.
In order to fight the malicious relayer, we are forced to find a way to sign counterfactual transactions without actually knowing the private key of the deployment wallet. In other words, we must send a transaction from a wallet neither we or the rest of the network knows the private key. Seems impossible right? Well not exactly…
Brace yourselves, cryptography is coming. It actually came in the form of the work of Nick Johnsons method of tampering with signed transaction while maintaining it valid. Basically, you can take one part of the transaction signature, substitute it with a certain random number and the transaction is still valid. You can read more about it in EIP-820. Here is how we did it.
1. You first create a regular deployment transaction from your own relayer account. You then override the gasLimit and gasPrice and sign it.
In the end of this sequence you end up with a long string — the signed transaction. Looks something like this:
2. Counterfactualising the transaction
Once we have the long transaction string we are ready to sparkle some magic. You remove the last 134 characters of the transaction and then append the following magical string:
Then we add random 64 characters starting with 0.
Let’s recap what we’ve done here. We first introduced the concept of relayer and how you can use it to pay the transaction costs for your users. This, however cool, the concept is rarely applicable and we introduced the concept of relayer-for-profit. After that, we’ve worked on securing both the user and the relayer against mischief. Last but not least we sparkled some counterfactual magic in order to ensure that the relayer is safe, but the user can still have new identity contract created.
Decentralizing the Relayer
Read about decentralizing the relayer in the upcoming Part Three