Open mhluongo opened 5 years ago
@gakonst this is a significant change to the state machine, and a bunch of stuff is floating still (eg the rewards for calling out undercollateralized deposits)
@prestwich could use your eyes here
cc @liamzebedee thought you'd like this
Build a maintainer to challenge deposits based on prices from popular exchanges
Probably want to integrate this into an existing executable (e.g. tbtc-maintainer), otherwise we're upping the operational overhead of running tBTC in rather annoying ways.
Build a maintainer to challenge deposits based on prices from popular exchanges
Probably want to integrate this into an existing executable (e.g. tbtc-maintainer), otherwise we're upping the operational overhead of running tBTC in rather annoying ways.
We'd also want to put active arbitrage in as a feature of an existing executable
We'd also want to put active arbitrage in as a feature of an existing executable
This scares me a bit... interacting with eg exchange APIs as well as people's wallets.. I dunno. I certainly plan to do it with our own funds though
Well yeah, but think of it from a reliability perspective. Giving people optional arb modules makes it more likely people will arb
(long comment ahead)
Agreed that any feature which requires an actor to perform some activity based on off-chain data should be part of tbtc-maintainers
.
Secret management is the risky thing here (API / private keys), so I'd recommend adding HSM support for anything requiring Ethereum Private Keys (https://github.com/certusone/yubihsm-go is a nice fit here, Loom had secp256k1 signer implementations for it). Not sure how you envision the deployment of tbtc-maintainers
in the cloud, I'd expect that Keep would operate multiple of those in the beginning, so you want some infrastructure for distributing secrets in a secure manner, in which case Sops is a great choice: https://github.com/mozilla/sops, https://www.youtube.com/watch?v=V2PRhxphH2w
As for the 3 mechanisms described, I'm writing up an example for each (which will probably be transferred over to the design doc), to make sure we all are on the same page. I also describe how there is no need for a deposit oracle, since users can simply choose to abort (but possibly forfeit a deposit bond)
*Note: fair, does not have to be 75 ETH. If the signers opted to created a Deposit that's only backed by 70 ETH, that's up to the Depositor to choose if it's sufficiently collateralized for their risk model.
The tradeoff of the aforementioned mechanism is the potential griefing of a Depositor or of a Signer:
The above describes an idea for removing the oracle from the deposit procedure, by introducing a constant size griefing attack vector by allowing the user to choose whether to accept or reject the quote provided by the signers, allowing users to grief signers if there's no bond, or signers to grief users if there's a bond. This should require no code additions on the smart contracts.
COURTESY_CALL
, by sending 61.99 ETH to the smart contract. Before T
, the 61 ETH can only move if a swap is performed for 1 BTC.(implementation detail: HTLC-swap vs Summa SPV proof swap)It does make sense as @prestwich said to add an arbitraging module in the maintainers, since if the arbitrageur is also the maintainer their returns are >=0 in all cases (sans gas fees). If a maintainer is not actively arbitraging, then in the case that ETH recovers enough to get the deposit out of the courtesy call, the maintainer loses the spread which can be bad.
This approach assumes that there's somebody interested to actually be the maintainer/arbitrageur, so the return must be attractive enough to justify these types of actions.
The above does not prevent from the case that Mallory is a malicious maintainer/arbitrageur, who triggers the undercollateralization challenge and instantly fills it. We need the ability for multiple parties to be able to post orders, and we should pick a price among them.
This can be fixed by allowing multiple swaps of ETH for 1 BTC (facilitated by an atomic swap or SPV proof protocol as before) during the fixed period of time T. Users place their bids for 1 ETH and the contract functions as a ETHBTC exchange with an on-chain order book.
An attacker in this case could try to empty the order book right before the
expiration period, and then set an arbitrary price of their choice.
The price is determined by the HIGHEST OPEN order at T
,
The liquidation fee is split evenly among the parties that were part of the winning bid.
If at any point the orderbook is cleared, then extend the duration of the process by T
. If >N
orderbooks get cleared, fallback to an aggregate between the mean/median of the price given by each of the N orderbooks (plus Maker oracle potentially). If M<N orderbooks get cleared, the price is the mean/median of the price given by each of the M orderbooks.
Note: fair, does not have to be 75 ETH. If the signers opted to created a Deposit that's only backed by 70 ETH, that's up to the Depositor to choose if it's sufficiently collateralized for their risk model.
This reasoning breaks down without a holding period on deposits- otherwise a depositor and signers can collude to get cheaper liquid TBTC. Imagine the depositor was okay with a half-collateralized deposit, then walks with their TBTC while the signers can walk with their BTC- the loser is the total system collateralization, not either party.
That's why I've considered an optional fall-back to a centralized oracle- to avoid having to wait hours for a deposit to be "sufficiently unchallenged" to make minting TBTC safe if there's not a tight enough order putting a bound on the price. Challenges will still fix the issue, but it can be used to prevent crazy attacks without killing UX and requiring waiting periods when there's not already a less-bonded unchallenged deposit.
An alternative might be to require that bonds use a price greater than 85% of a centralized oracle, or suffer a waiting period... which gives us nice behavior if the oracle were to be removed via user action.
@prestwich Most of my comment above is me unpacking the mechanism in longer form, what do you think about the extension of the challenge duration if the orderbook gets cleared w/ optional fallback to centralized oracle?
An attacker in this case could try to empty the order book right before the expiration period, and then set an arbitrary price of their choice.
I think the way to look at this might be "there is no price without a ceiling" rather than fill prices giving us data @gakonst - in fact, unfilled orders are what give us an assurance here
The order book approach
This can be fixed by allowing multiple swaps of ETH for 1 BTC (facilitated by an atomic swap or SPV proof protocol as before) during the fixed period of time T. Users place their bids for 1 ETH and the contract functions as a ETHBTC exchange with an on-chain order book.
An attacker in this case could try to empty the order book right before the expiration period, and then set an arbitrary price of their choice. The price is determined by the HIGHEST OPEN order at
T
, The liquidation fee is split evenly among the parties that were part of the winning bid.If at any point the orderbook is cleared, then extend the duration of the process by
T
. If>N
orderbooks get cleared, fallback to an aggregate between the mean/median of the price given by each of the N orderbooks (plus Maker oracle potentially). If M<N orderbooks get cleared, the price is the mean/median of the price given by each of the M orderbooks.
this is not how I'd describe the mechanism. I think the introduction of a sampling interval T
is unecessary. Instead, how about as follows:
Many ETH/BTC orders sit open on the books. Each order is marked with a timestamp of when it was placed. The price is the HIGHEST OPEN order whose timestamp is more than 90 minutes in the chain's subjective past.
Because cross-chain communication has ~1 hour of time lag (in the best case), our order book's on-Ethereum state is always ~1 hour behind its true state (which is to say, orders that have been filled will show as open during that time period). This means that an order sitting on the book 90 minutes has had an opportunity to be filled, but has not been filled. This means that the order does not represent a fair market price. The HIGHEST such order is therefore an objective bound on the fair market price.
However, suppose that an attacker wishes to push that bound. She could fill all open orders, wait the 60 minutes, and affect the price for 90 - 60 =
30 minutes. To prevent this, we have to allow observers to add liquidity to standing orders without waiting 90 minutes. This means that an attacker would be forced to fill an unbounded amount of off-market orders from any number of arbitrageurs. The more she wants to move the price, the higher the spread she pays to each arbitrageur to do so. Assuming the arbitrageurs collectively have more capital than the attacker, she can't effectively move the price.
A naïve question - what drives liquidity for these auctions? With a trusted price feed, we are assuming some vested interest from those maintaining it. What makes this ETH<->BTC market better than using Binance for example?
A naïve question - what drives liquidity for these auctions?
We'd have to bootstrap this marketplace. and possibly sell ETH periodically ourselves
With a trusted price feed, we are assuming some vested interest from those maintaining it. What makes this ETH<->BTC market better than using Binance for example?
They may not always have an interest in maintaining it "correctly." This removes our need to rely on the maintainers
I'm taking a crack at spec'ing this as it's closely related to #293, which is blocking a couple projects we're working with from integrating cc @gakonst
This has been delayed to v2. MakerDAO's Medianizer will be used for v1.
The system needs price information at two critical times
1) When a new
Deposit
is created, the system needs to know how much collateral should be required of signers. If the collateral is allTBTC
, no feed is necessary. 2) The system needs to know when aDeposit
becomes undercollateralized, as well as "how much" to decide whether to move to the "courtesy call" or "liquidation" state.Challenging deposit bonds
Let's ignore the first case for a moment, and focus on the second. Rather than treating price as a universal aspect of the system, we can focus on whether a particular deposit is undercollateralized. Anyone may challenge that a particular deposit is undercollateralized, and put some money down to prove it. If they're shown to be correct, they're paid a fee from liquidation.
To prove they're correct, the challenger puts down a bid for 1 BTC in ETH. We expect that the challenger will choose a favorable bid, rather than what they believe is the exact price, so if the bid is taken the challenger will make money. If the bid isn't taken within a timeout, we now have a ceiling on the BTC/ETH price.
Generalizing to an orderbook
Generalizing this idea further, we can build a 1-sided cross-chain orderbook of bids for BTC. We can consider the "price ceiling" of BTC as the deepest bid in the orderbook that has stood unfulfilled for some timeout. Of course, we don't want that price to move very often.
An attacker who wants to move the oracle price will have to eat through the orderbook, suffering price slippage- but a well-funded attacker can always defeat a thin orderbook. To prevent this, arbitrageurs must be allowed to insert bids into the orderbook before an attacker can clear out the book, effectively frontrunning an attacker.
New deposits
To decide the bond requirements for a new
Deposit
, all this scheme requires is a single bid that's been open long enough to be considered a legitimate price ceiling.That time, however, might need to be as long as 24 hours- which leads to a poor deposit UX.
In the case that a price ceiling hasn't been established by the one-sided orderbook / challenge system, I suggest we fall back to Maker's existing BTC/USD and ETH/USD price feeds for new
Deposit
creation. If the feeds ever fail or aren't updated, we can fall back to the orderbook system.The reliance on Maker feeds should be easy to turn off via user-activated governance. I'm working on better describing how that might look, but in the meantime a single privileged call to disable the Maker feed behavior by the contract owner should do it.
Thanks to @danrobinson @prestwich @nanexcool @afdudley and many others for the ideas behind this mechanism.
Tasks
Replaces #92