Haven Deep Dive: Cracking the xUSD Code
The following is authored by @cryptodweab, Haven’s Project Lead
Neac (Haven’s Protocol Lead) and I have spent the last several months preparing for the mainnet launch of xUSD. Throughout our testing, we’ve focused on how to most securely verify xUSD exchanges to ensure a smooth launch and long term success for this first private stablecoin.
This work has resulted in extensive redesigning of Haven’s codebase. As we learned in our testnets, Monero’s structure proved uniquely challenging for signing and verifying xUSD transactions. It has been tedious, slow, and at times downright frustrating. But we’re proud to say we’ve developed an innovative solution that will ensure the accuracy, reliability, and privacy of xUSD transactions now and in the future. Below is a deep dive into the process that led us to this solution.
Haven’s first testnet calculated an approximate value to tell users how much xUSD they would receive for the XHV they requested to exchange. This is because in Monero (and Cryptonote), when users submit a transaction, they are sending a commitment to a number of coins: both inputs and outputs. These inputs and outputs need to be equal or the transaction will always fail.
This Monero structure became a problem for Haven when we began implementing xUSD exchanges. Initially, we could not guarantee the final amount of xUSD exchanged would equal the approximate amount of xUSD quoted before a transaction was submitted. This is because the XHV price changes dynamically. The XHV price (and thus, the xUSD exchange rate) could change between the time a user submitted a transaction and when the transaction was confirmed.
The first way we approached a solution to this challenge was by no longer looking for equality of inputs and outputs. But in order to prevent manipulation, we needed the sender to be unable to specify a false amount. So we decided to take this step out of the sender’s hands entirely and give that responsibility to the miner.
This solution, implemented in a subsequent testnet, added a delay in providing the exact xUSD exchange rate. The sender would have to wait for a block to be mined, and the miner had to include the exchange price in the block header. However, there were holes in this solution — nasty ones.
Imagine an adversarial attacker with both the money and the knowledge of this part of Haven’s code. After setting up the infrastructure to obtain 51% of Haven’s hash, they could become both the miner and sender and thus set their own xUSD exchange rates. This possibility was, shall we say, suboptimal!
So in the next testnet, we decided to move the responsibility back to the sender, but only so they could take the last verified pricing record already in Haven’s chain (the pricing record from the top block) and specify which block they used in the transaction. This could be checked and verified by miners and daemons, who could also check that inputs vs. outputs were equal not in coins, but in dollar value (critical for xUSD).
This proof of value became the key to Haven’s success. But the model described above also proved vulnerable to attack. Several lessons came out of the next few xUSD testnets conducted with our team and community. First, we learned that a shorter chain could game the system. Second, we learned we needed a way to show the circulating supply of xUSD (and future xAssets) or we could never determine the health of Haven’s network.
The next stage of development took us down a different path. As stated above, proving a transaction’s value was the key, and we needed to replace the proof of Monero inputs = outputs with the proof of dollar value for xUSD exchanges.
We tried over a dozen different proofs, checks, and validations. But we found that unless you’re actually sending XHV and no other type of asset, which in private testing is what we were doing, the whole system gets confused after a few backwards and forwards transactions. The more complex the exchanges, the harder it got to verify a transaction’s dollar value. Then throw in multiple transactions, which needed to span large amounts of inputs, and things got really tangled.
These challenges were all related to the way commitment signatures work, which is an absolute requirement in Monero. Unless your commitments are valid, you can never verify the money hasn’t been created from nowhere.
At this point, we decided to review the old model of “colored coins” which has previously been layered on top of Bitcoin, creating a new set of information about coins being exchanged. Using colored coins, transactions could be “colored” with specific attributes. This effectively turns colored coins into tokens, which can be used to represent anything. But — and this is critical — colored coins can only work if you’re not tied to Monero’s commitment structure.
So that is the challenge we undertook: modifying Monero’s commitment structure to allow for the use of “colored coins” which can distinguish between XHV, xUSD, and future xAssets on a single Monero-based chain. And I’m proud to share that for the first time, we’ve done exactly that.
We had to replace Monero’s multilayered linkable spontaneous anonymous group (MLSAG) signatures with the newer compact linkable spontaneous anonymous group (CLSAG) signatures to accomplish this. But with a little help from one of Monero’s top developers, Sarang Noether, the solution is now working as intended. We’d like to thank Sarang for his assistance in helping us navigate this new encryption scheme.
So the result of all this testing and tinkering is the following: we have created a new transaction structure with genuine inputs as XHV and genuine outputs as xUSD. To convert from XHV to xUSD, we actually give XHV as inputs and xUSD as outputs. Daemons then check that the number of outputs is equal based on the exchange rate given in the block the sender specified as the pricing record. This is done purely by summing the commitment masks and the exchange rate independently retrieved by verifiers.
The logic is that a verifier will always get its own exchange rate. So the only way a sender can get a transaction through is to know both that exact exchange rate and the exact number of inputs (which only they can know since it’s in their wallet and unreadable elsewhere). The verifier then sums the output commitments, and taking into account their independently gained exchange rate, compares that to the inputs. They must be equal to pass. Of course, all values are encrypted so no one can see or alter the amounts.
Note: the sender can only use an already verified pricing record, which are all currently created via Chainlink’s XHV oracle. Daemons read the pricing record number in the transaction and get the record (the actual price) from the block header. This means prices are already immutable at time of use.
This is exactly the type of solution we were searching for after contemplating how to address challenges associated with xUSD exchange verification in the very first testnet. This solution is working nicely in our private testing and will be shared in our next public stagenet in the coming days.
Assuming no other issues are identified in testing, this is the solution we will use to launch xUSD live on Haven’s mainnet. As a part of the launch process, we look forward to sharing publicly the xUSD exchange code we’ve developed in private over the last year to create the first-ever private stablecoin.