On the Fee Schedule

fsefse Member Posts: 2
I'm quite new to ethereum but after reading the white and yellow papers I must say I'm very excited about it! I, however, have a worry about the fee schedule. My background is in economics and I tend to focus on how such a schedule incentivizes people rather than the more computer science oriented parts. From that perspective I see three potential problems with the current schedule. Being new to this, I hope I've not misunderstood anything or missed if this already is a ongoing discussion.
  1. Data storage. Storing data in the ethereum blockchain is extremely cheap and there is no incentive to remove data that is not needed. This is potentially a huge problem as, unlike most other blockchain implementations, any data can be stored in it and therefore runs the risk of being exploited.

    As a, maybe not completely fair but still illustrative, example: Storing one 32-byte word in Amazon's AWS for one month costs more than 800 000 times the cost of a single ordinary computational operation (see below for calculation). In ethereum the cost of storing a 32-byte word indefinitely is only 200 times the cost a computational operation. This is of course only the relative price, the absolute price is likely to still be lower at Amazon. This, however, incentivizes developers in ethereum to develop applications with a space–time tradeoff that does not reflect the true costs.

    An example of this would be any Dapp with a bit of complexity (and it does not need to be much) which then would benefit from caching all incoming calls. E.g., a Dapp that require 4 000 fuel to complete, could for an extra 200 one-time fuel cost reduce all subsequent calls to a mere 20 (a single SLOAD operation). Imagine if most Dapp start caching every call and what that would do to the size of the blockchain.

    This is made much worse by the fact that there's no incentive to remove information once it's stored. The yellow paper states that the fee for clearing memory is waived (or rather payed upfront). The effect of this is that it is slightly cheaper to overwrite storage rather use new space. But it provides no incentive to clear unused space -- actually it does the opposite. By clearing storage one would need to pay 200 fuel to reclaim it in the future. By not clearing it one would only pay 100. And in any case, why throw away free information that one might need in the future (even if the probability that one need it is very small)?

    Since there is no incentive to clear memory each Dapp is likely to weakly increase it's storage use over time, leading to an explosion of the blockchain size. This is especially true for data feed Dapps that are likely to have a varied storage need. For example, an app providing all major Bitcoin transactions the past day will sometimes need to store millions of transactions and sometimes only a few thousands. Since the app developer knows that storage needs varies and than clearing memory will lead to future costs (but that it's free to keep the information), the storage of the app will be the maximum of the storage need at any time in its history.

    Suggestion: The classical suggestion from an economist in these cases is that the price should reflect the cost. In this case the storage cost is not one-off as the the price (the 100-200 fuel for a SSTORE operation) but rather a continuous storage cost. Instead a more appropriate schedule would be to reduce the cost of putting information into storage to at, or near, the price of a normal operation and impose a continuous cost. For example all accounts could be forced to pay a couple of wei per stored word per generated block. When an account cannot pay for its storage it is removed. (This could be extended so accounts need to pay also for its stored code as well, which would help to keep the block state clean).

  2. Stack versus memory. If I understood this correctly, the computational stack is unlimited and free which memory is not. This will push developers to be overly stack focused and lead to inefficient Dapps. Why use memory when one can store all the information one want for free in the stack? Of course this problem is mitigated by that many tasks would require more other operations when solely using the stack compared to also using memory and by that one still has to pay for an ordinary operation to push to the stack, but the problem is by no means solved.

    Suggestion: Again, making the price reflect the cost is usually a good way forward. The stack and (virtual) memory require approximately the same amount of (machine) memory and thus should face the same price. A question is however whether this should be as now where one pays to use memory. The stack (unlikely memory) is likely to fluctuate greatly making it very costly to increase the price of pushing to the stack. An alternative would be to keeping track of the maximum combined memory (i.e. virtual memory + stack) used at any time during its execution and base the fee on that. The app would then instead be terminated if the maximum memory counter exceeds the remaining fuel.

  3. Calling costs and modularity. One of the great possibilities of ethereum is its modularity. Someone can implement the equivalent to a software library with all kinds of functions and data feeds that other Dapp can call. However, if the transactions costs (i.e. calling costs) are too high, people will not do this and this will reduce the modularity of the network. This is especially true since everything on ethereum has its machine code in the open. Thus, even if a Dapp takes a fair price, too high calling costs will lead to developers that copy-paste functionalities directly into their Dapps leading to very large Dapp code bases.

    Suggestion: I've not really researched this but to me the calling costs seem a bit high (20 fuel for a call and 5 fuel per byte information transferred). Reducing this as close as possible to the 1 fuel for an ordinary operation would be ideal and I don't really see the need for a fee for information transfers (over-and-about the ordinary memory costs). However, a call necessarily imposes quite a bit of overhead (fuel calculations, program initiation, etc). One way around all of this would be to introduce different kinds of calls. For example, one could have one call that is well-separated as now and have the same cost as now. And then a cheap direct call with much less overhead, e.g., sharing fuel, memory etc.

(The Amazon AWS calculation: one GB of data costs $0.1 per month, which is $(32 * 0.1 / 1024^3) per 32-byte word per month. A T2 micro instance costs $0.013 for an hour which gives 6 minutes of 100% utilization of a "High Frequency Intel Xeon Processor operating at 2.5GHz". Assuming 4 operations per clock-cycle this yields $(0.013 / 6 * 60 * 4 * 2.5 * 10^9) per operation. The ratio is then (32 * 0.1 / 1024^3) / (0.013 / 6 * 60 * 4 * 2.5 * 10^9) = 825 295.1.)

Comments

  • JasperJasper Eindhoven, the NetherlandsMember Posts: 514 ✭✭✭
    Also think the fee system needs more work, especially on the data storage. There is this blogpost about requiring 'rent' on data storage.

    The code is compressed somewhat, similar code wont take as much space as the code is long. That said, it might be handy to have reduced call costs if the contract was recently called. (An idea is to have a call cost and a contract code loading cost, per-transaction, probably adds a bunch of complexity though, particular in the direction of potential accidental forks.) Also, you might think that a contract could save by such and such, but contracts have the execute code that implements it. So you cant always just reuse some data for something else later, because then the contract first has to remember to do so, costing gas potentially defeating the purpose.

    Also note that even deleted contract storage cant be thrown out immediately, at best the state has to be rewindable in case the current block turns out not to end up in the main chain. Also, enough people have to hold all of history, so if someone news comes, (s)he can check that the whole history indeeds comes out of the genesis.
  • fsefse Member Posts: 2
    edited September 2014
    Jasper said:

    Also think the fee system needs more work, especially on the data storage. There is this blogpost about requiring 'rent' on data storage.

    Thanks for the tip, interesting stuff. I suspected that this had been in the loop before.
    Jasper said:

    The code is compressed somewhat, similar code wont take as much space as the code is long. That said, it might be handy to have reduced call costs if the contract was recently called. (An idea is to have a call cost and a contract code loading cost, per-transaction, probably adds a bunch of complexity though, particular in the direction of potential accidental forks.)

    I'm a bit unsure what you are referring to here, but I guess it is to my point 3. My idea was not so much the loading-from-blockchain-overhead (but that is also a good point) but rather contract separation. Imagine the follow pseudo-code of a contract calculating factorials by only using the stack, which presume a type of call that shares the memory/stack/etc with the caller:
    PUSH(0);
    SWAP();
    WHILE PEEK() > 1:
      PUSH(PEEK() - 1);
    SWAP();
    WHILE PEEK() > 0:
      PUSH(POP() * POP());
      SWAP();
    POP();
    To call this one would simply push the number one wants factorialized and make one of those non-separating calls to the contract. This would probably be much faster than the current way to implement and call such a contract. Of course, no one would manually use these calls, but contracts can be used as an abstraction for (programming) classes and, with the current improvements to the ABI, code libraries could soon be feasible. These fast/unsafe/non-separating calls would be of great use in those cases. But I admit that this idea needs much more thought.
    Jasper said:

    Also, you might think that a contract could save by such and such, but contracts have the execute code that implements it. So you cant always just reuse some data for something else later, because then the contract first has to remember to do so, costing gas potentially defeating the purpose.

    Again, I'm a bit unsure what you are referring to. But here I assume it is to my caching example. As you say, caching will not always be beneficial but it can usually be done very cheaply so that the contract need not to be very complicated or have a lot of recurring calls to do it. Consider the following pseudo-code of a contract that implements caching for a load cost of ~40 and save cost of ~220, way less than the likely fuel comsumption of a typical contract:
    cached_output = data_storage[sha(input_data_array)]
    if cached_output != 0:
      return cached_output
    else:
      *** Some operations burning fuel ***
      data_storage[sha(input_data_array)] = output
      return(output)
    This assumes that feeding "input_data_array" to the contract always will produce the same output. That is of course not always the case, but then some more complicated caching function could probably still be used. My point is simply that since the price does not reflect the cost caching will be overused.
    Jasper said:

    Also note that even deleted contract storage cant be thrown out immediately, at best the state has to be rewindable in case the current block turns out not to end up in the main chain. Also, enough people have to hold all of history, so if someone news comes, (s)he can check that the whole history indeeds comes out of the genesis.

    Well, keeping unused/unpaid contract storage a couple of block generations extra is better than keeping them forever. And keeping the complete history will not be feasible if ethereum is to see any substantial use. That would be the equivianlent of saving a (delta encoded) snapshot every ~15 second of every server: information will explode. Look at the size of bitcoin block chain and consider how very different the information storage will be with ethereum. Why not just require that nodes keep enough blocks to make unrewindable forks essentially impossible? Anyone joining the network has, by doing so, in effect agreed with the current state. Of course, someone could save the complete history if they wanted to, but to require that of, e.g., full nodes would result in that there would be no full nodes after a while.
  • JasperJasper Eindhoven, the NetherlandsMember Posts: 514 ✭✭✭
    I think it was a deliberate choice not to just give the stack to a called contract. Think because it possibly introduces bugs where you dont know if there will be enough elements in the stack. It can be done, at least currently, i see a JUMP and JUMPI, i think it could -basically- a function. (looking at cpp-ethereum/libevm/VM.h) (Note that contracts are themselves closures, "functions with state"s.)

    I basically agree here, btw, either the gas cost has to be much higher, or there has to be some kind of limited duration.

    The What if Ethereum lived on a treap blogpost is relevant too.
  • VitalikButerinVitalikButerin Administrator Posts: 84 admin
    So, CALL is going to be even more expensive in the future. Setting up a new VM context is computationally hard, deal with it. But then, JUMP and JUMPI are gonna be expensive too.

    As for contract storage, we're not implementing rent (due to complexity, and because we want dapps to be able to rely on other contracts existing forever), but we are adding a gas incentive to clearing storage, which should achieve partial incentive-compatibility.

    I don't see anything wrong with the stack being free. You can't really use stack as a substitute for memory, since you can only access it 16 levels down and pretty much all mem-intensive contracts that I know about potentially require any data from memory to be quickly accessible.
  • stevexstevex Member Posts: 4
    I too am very interested in the actual economics of the operation in practice. Is my understanding below correct?

    In one (or more) of the examples the transaction price is a function of factors including gasprice and bytefee.
    Is there any notion of what values these will actually have to get processed? i.e. how many wei?

    I read "How gas is priced depends on the global consensus of the community. It's therefore likely operations that have the best priced gas will be executed first on the network, and the rest a bit later."

    That would seem to suggest that response time for processing will be governed by some market rate. High "priced" transactions (where gasprice is a high value) effectively provides an incentive for the transaction to be processed (by the miners) at a higher priority than lower "priced"transactions. Does that then mean the confirmation time for a specific transaction will depend on the rate offered for the transaction processing (gasprice)?

    Presumably then there could be a tiered market price for transaction processing, of the form pay x get y second confirmation.

    Presumably also there would be peak and off peak rates? In terms of if y processing time is desired then pay x1 at time1 and could pay x2 at time2?
  • VivaLaPandaVivaLaPanda Member Posts: 4
    In my post here, I asked about the possibility of using contract activity instead of rent. I feel that would work better, because the contract's users incur the fees, not the user who registered the contract. This isn't perfect because large contracts that only use a small amount of code won't be paying for that larger storage space, only for the code that is being processed. However, besides that it seems like a good idea, in that it basically means miners are only storing contracts which are likely to generate them revenue. Vitalik said that we can't let contracts expire
    Vitalik said:

    because we want dapps to be able to rely on other contracts existing forever

    ,but that doesn't seem right because he also states that they are
    Vitalik said:

    adding a gas incentive to clearing storage

    ,which means that contracts being deleted IS incentivized. If you expired contracts based on activity, not rent, then only contracts which aren't being used will be removed, so contracts relying on other now-expired contracts seems fairly unlikely, because if that was really a necessary contract, it would have been activated more often, however that assumes that the expiration time fits the system. Deciding the expiration time is the main issue, whether a date like 3 months should just be chosen arbitrarily, or else a dynamic system be used to determine expiration times.
  • JasperJasper Eindhoven, the NetherlandsMember Posts: 514 ✭✭✭
    because if that was really a necessary contract, it would have been activated more often, however that assumes that the expiration time fits the system.
    Actually quite a lot can be achieved with the threat of something happening. For instance instead of calculating the value, you could save gas by claiming the result, and putting in some stake that will be taken if someone else challenges, calculating the result, and it turns out wrong.

    Such calculations might not run much at all. Of course, you could say that you put the responsibilty to contract developers. If accessing contract storage is cheaper than calling them, they might decide to use a storage slot to decide if the contract needs poking.
  • cryptoboycryptoboy Member Posts: 74
    In Amazon's case, the code only runs once, on a single instance. In Ethereum's case the code may need to run at least once on every instance.
Sign In or Register to comment.