Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use of BTCRelay without paying fees #44

Open
holiman opened this issue Mar 21, 2017 · 8 comments
Open

Use of BTCRelay without paying fees #44

holiman opened this issue Mar 21, 2017 · 8 comments

Comments

@holiman
Copy link

holiman commented Mar 21, 2017

With Metropolis, there will be a new opcode, REVERT (ethereum/EIPs#206) and RETURNDATA (ethereum/EIPs#211).

The REVERT opcode will function as a throw, reverting state-changes, but will not burn the remaining gas. Additionally, it can return some data, in order to provide the callee with information about the error.

Unfortunately, this EVM-change undermines the model for synchronous oracles which accept payment; it's possible to

  1. Make a call to BTCFreelay
  2. Make a call to InternalCall
    3. Perform call to BTCRelay for verification, along with payment
    4. Perform revert along with the response from BTCRelay

This would revert the payment(s), but still grant access to the return value.

Note: Even without RETURNDATA , it would still be possible to perform this attack, albeit with less data being extracted. Example gist showing how to 'freeload' on btcrelay using revert without returndata: https://gist.github.com/holiman/51f9b02b64f864b896129d329757460c .

Note 2, this can also be performed already today, using a regular throw, but the attack is quite 'messy' since it needs to handle several intricate cases of OOG due to remaining gas being discarded at every throw.

I'm filing this as an issue, even if it's not yet implemented, so there can be a discussion about possible future modifications that can be made. Also, posting it as a 'known issue' here makes in not eligible for a reward in the bug bounty.

@tawaren
Copy link

tawaren commented Mar 22, 2017

I think that it is a good case if something that is already possible especially if messy and complicated becomes easier to do, because future smart contracts get safer because the developer and potential auditors are more likely to realize that something is flawed when their is an easy (or in this case at least easier way) to exploit it.
The proposed INJECTED_STATIC_CALL if accepted would make it totally obvious that such a concept is flawed in the EVM

@holiman
Copy link
Author

holiman commented Mar 22, 2017

If by INJECTED_STATIC_CALL you mean STATIC_CALL, actually, STATIC_CALL will be unable to utilize btcrelay, since any form of payment modifies the state, which would immediately error out.

@tawaren
Copy link

tawaren commented Mar 22, 2017

No I meant ethereum/EIPs#199 (which is only an issue but I used it as an illustration of what would make such flaws the most obvious)

@axic
Copy link
Member

axic commented Mar 23, 2017

Please note that this is an issue even today, without REVERT, though one must figure out an average cost for verification and risk that amount. See an example code here: https://gist.github.com/axic/0b262ff6813ac8c3971ff399f2e286ce

@ethers
Copy link
Member

ethers commented Mar 24, 2017

Thanks for the discussion. It's looking like the design for such oracles should use 2 transactions. tx1: is for some payer address to be recorded. tx2: when oracle is asked for information, it checks the payer has prepaid (tx1). Implementation wise, one way to ensure 2 txs is to also record the blocknumber in tx1, which tx2 checks that its blocknumber is different.

@tawaren
Copy link

tawaren commented Mar 24, 2017

I think that won't help ether, as long as the information is on the chain their is a way to access it. One approach that always works is, that I read it outside the chain and then generate a proof, that in a specified block at the storage key inside the oracle contract a specific value was stored. Because smart contracts have access to the block hashes, they can verify that proof. That way not a single call to the oracle is necessary.

The only way I see to make a oracle work, is to not charge for reading the information but for generating the information as oraclize.it does it. So the information is not send to the blockchain until it is requested and payed for.

@ethers
Copy link
Member

ethers commented Mar 24, 2017

read it outside the chain and then generate a proof...

The proof/s would be stored on-chain and would thus have costs. Exact economic costs aside, the "altruistic" party that wants to make BTC Relay free for use, may as well use those costs to submit Bitcoin blockheaders instead (and set no fee). EDIT: Or for a "malicious" party, the cost of storing the proof might be higher than just paying the fee.

There's potentially 2 types of "on-chain oracles": for-profit and non-profit. Business models for on-chain for-profit oracles may be tenuous or infeasible since the data is public (as @tawaren and others suggest). The fundamental reason for fees in BTC Relay is to strive for sustainable, decentralized infrastructure. BTC Relay needs data regularly and apart from "altruistic benefactor/s" which may not exist, it needs to incentivize the community to provide the data, and the obvious way to do it is to charge a fee (at least for first use of the data).

My understanding is that Oraclize isn't 100% trustless; maybe increasing its trustlessness is more feasible than a sustainable, decentralized infrastructure. If a decentralized one works, it may have lower cost than a centralized oracle infrastructure (and also the advantage of availability guarantees of the blockchain).

@MicahZoltu
Copy link

Oracles on Ethereum aren't valuable because of the information itself. They are valuable because they make that information immutably available to contracts via a well defined trust scheme. When one pays for an Oracle, they are paying for the mechanism of getting real-world information into a contract with some well defined level of trust (depends on the oracle).

This opcode will undermine any system that charges for on-chain access to that data to more than one user. It will make it so that once someone has paid to put the data on-chain, any contract can access that data on-chain for free. While in some cases "pay to put on chain" can work, this doesn't allow distribution of the cost across many users. If you have some kind of data that is expensive to put on the chain you run into a problem where the user that puts the data on-chain has to pay 100% of the cost and the next 1000 users get it for free.

At the moment, I don't believe there is a live exploit because throw burns all gas, so there is no way for the contract to take the result and store it on-chain.

As an example:

  1. Ask Oracle for results (pay the fee).
  2. Store the results in a local variable (on stack, not subject to revert).
  3. Revert.
  4. Read from local variable.
  5. Store the data on-chain.

Currently, step 3 would be a throw which would prevent step 4 from completing. This allows an oracle to distribute its costs across all users of the data rather than having to front-load the costs on the first request. This change, as I understand it, will effectively allow for a contract to be able to copy the result of any other contract without paying the contract for that result.

I would consider this a pretty major change in the model and it violates expectations of historic contract authors. My recommendation would be to make it so if you call revert, your stack is wiped rather than allowing the contract to retain its stack on a call to revert.

Of course, one can argue that the first person can pay the low-cost for the data and then publish it for free to everyone else but this can be mitigated by a semi-ponzi scheme where the first person who wants the data pays full price, then the next person pays full-price minus delta to the first person, the third person pays full price minus 2*delta to the second person, etc. This ensures that the contract always gets enough to cover its costs, while making it so if the data is highly sought after everyone will end up paying delta for the data. Similar schemes could be developed to have all parties who want the data pay a bond up-front, then after some time-delay they will be refunded such that they all pay the same amount (e.g., automated group discounts).

The point of the above two examples are just to show that it is possible to setup an oracle that distributes costs to users of the data without risking itself being underpaid for the service.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants
@axic @holiman @MicahZoltu @ethers @tawaren and others