EOSIO / eos

An open source smart contract platform
https://developers.eos.io/manuals/eos
MIT License
11.27k stars 3.6k forks source link

Kidnapping RAM of users / Allow users to manage their RAM #5376

Closed kesar closed 4 years ago

kesar commented 6 years ago

A malicious contract could take all the RAM of an user without let the user get it back. Contract could check RAM available in user and just create some random string/data to fulfil it

There may be some shitcoins that the value of the RAM stored is higher than value of the token.

Suggestion:

Users can manage their RAM storage allowing them to remove RAM or to change back ownership to the contract.

Extra:

eosio.token as seems to be the standard could have a method where user burn / remove token from his account to clean RAM.

xJonathanLEI commented 6 years ago

While I agree with you that users should have control over their own RAM usage:

As for the eosio.token part, I believe the contract already allows you to do this:

if( from.balance.amount == value.amount ) {
    from_acnts.erase( from );
}

All you need to do is to find some guy who already has token balance. It shouldn't be that hard.

qimiaoguo commented 6 years ago

see my issue https://github.com/EOSIO/eos/issues/5319

kesar commented 6 years ago

What if the user accidentally cleans his own balance record in some important contract?

Great points, perhaps its something that it can be managed through special permissions & some kind of garbage collector where after X amount of time you can reclaim your ram, with a 3 days period of time to revert / reclaim for other member if someone wants to keep data on their own name.

It shouldn't be that hard

For EOS probably, but I was thinking about garbage tokens where liquidity can be different, but I guess you are right, you could most likely send it back to the contract and your ram will be free.

wanderingbort commented 6 years ago

For better or worse, the idea of a user reclaiming misappropriated RAM will break many invariants that well-meaning contract developers depend on. At the very least, every time we have analyzed that as a solution we have concluded that it becomes extremely difficult (almost impossible) to author safe contracts.

I will break out a set of issues (and reference this one) for our proposed solution.

We consider the "problem" to be unexpected consumption of resources, as there are several use cases (most popular apps use some form of this feature) where one can argue that the user is consenting to some small amount of their RAM being used to fulfill the contracts need. Consider, putting tokens into a distributed exchange that needs an internal tracking row for your balances. As long as the contract is transparent about it, perhaps in the Ricardian clauses, we do not take issue with it. As the platform cannot interpret and enforce this transparency without somehow interpreting the knowledge the user had when submitting a transaction we have no technical mitigation for the fundamental issue.

We can however, provide a safer environment for users which lowers the burden of auditing every contract, and every contract that contract may notify.

Our current solution for that is to allow greater flexibility for users to declaratively attach assumptions to transactions. Assumptions like "I assume this transaction uses 0 bytes of my RAM" or "I assume this transaction uses less than 240 bytes of my RAM". We already have some explicit assumptions in a transaction header that limit cpu usage and net usage however, we seek to create a more flexible and extensible set of assumptions.

See the referencing issues for more details and please contribute comments on viability etc there.

kesar commented 6 years ago

I know its gonna be tricky, but I think the faster that we allow this, the better that dapps can handle this cases.

Not a single dapp should have the right to take resources of an user and not allow a mechanism to revert it.

In the long term this will lead into thousands of accounts storing garbage in RAM, RAM that could be useful for apps that have a real utility.

We could exclude for example contracts that are part of the core eosio.*, if those are problematics.

Problem with "I assume this transaction uses less than 240 bytes of my RAM", its that it should be "I assume this transaction uses less than 240 bytes of my RAM and I am not gonna get it back"

Perhaps the "erase-multiindex-action" could trigger an event that dapps contracts can handle, in that case, developer could decide what to do (for example, pay the ram). That event CPU / NET would be paid by the account who delete the action. onEraseRow(row, account) { if account != self { ... } }

heifner commented 6 years ago

Note #5339 includes account ram delta to each action trace. This makes it trivial to see the usage of ram per account for each action including notifications.

wanderingbort commented 6 years ago

Perhaps the "erase-multiindex-action" could trigger an event that dapps contracts can handle, in that case, developer could decide what to do (for example, pay the ram).

It would be trivial for a malicious contract to produce some sort of unrecoverable error that forces that transaction to abort. If we allow "erase-multiindex-action" to succeed even if the contract fails then we are back to the situation where contract developers must assume they will not be notified when the data is removed as many ephemeral situations could trigger it (like CPU resource exhaustion). We may as well not notify the contract developer as their base assumption for safety is that the data will disappear without notice and with that base covered, there is little value in a "better" mode.

IMO we learned this lesson with the "on_error" handling for deferred transactions which is a best effort notification similar to this and has resulted in harder to understand/maintain code with marginal actual benefit considering every contract must defensively code when using deferred transactions that have critical side-effects.

wanderingbort commented 6 years ago

Not a single dapp should have the right to take resources of an user and not allow a mechanism to revert it.

Without consent I agree. With consent, the idea that a dapp will hold and potentially never surrender RAM from a user's pool is no different than that dapp taking a non-refundable/voidable deposit.

Where I take issue is that we currently have no good way of expressing informed consent outside of authorizing the transaction as a whole which implicitly authorizes all the side effects which I may/may not be aware of. For this to qualify as "consent" we must place an extraordinarily high burden of audit on normal users. It is evident that this is not a practical long-term solution.

This is why we are trying to address means of expressing informed consent or bounding what users are consenting to implicitly rather than disabling the functionality outright.

The platform has to look at this situation broadly and not through the lens of a single, admittedly bad, user experience.

kesar commented 6 years ago

the idea that a dapp will hold and potentially never surrender RAM from a user's pool is no different than that dapp taking a non-refundable/voidable deposit

Then I would rather get asked to pay 0.003 EOS for the dapp for the storage rather than hide it in my RAM.

My Money -> My EOS -> My RAM -> My rules.

Dapps needs to be designed around that paradigm, where if data can't be deleted by an user, data shouldnt be paid by the user. A model where I need to stake EOS into a dapp contract and they will handle the RAM would make more sense. If I unstake, after X days, they can do whatever they want, and I can claim back my EOS with current ram price market.

Example:

Also a callback when row is deleted for dapps would help. Example:

Users should have an API where they can check their RAM storage and manage it.

The platform has to look at this situation broadly and not through the lens of a single, admittedly bad, user experience.

That's exactly what I am thinking, in few years, there may be tons of RAM garbage in the blockchain unless we manage RAM storage in a proper way.

wanderingbort commented 6 years ago

Then I would rather get asked to pay 0.003 EOS for the dapp for the storage rather than hide it in my RAM.

That is certainly a little more transparent and there is nothing stopping dapps from deploying that model today except inconvenient library support and complexity in the UX

What is lost by that model is the ability for a user to acquire RAM when that acquisition is "cheap" and then put it to use as they see fit unless we open the ability to transfer previously acquired RAM as well (explicitly rather than the current implicit transfer). Admittedly that can all be done by the system contract on most EOSIO based chains.

I'm going to see if I can get some internal commentary on that model. Existing chains have dapps deployed today that use the current methods so, this may not be enforceable on those chains without a significant backlash.

A model where I need to stake EOS...

I dont see any way to enforce that model at a system level. It can be best practices to return the deposited EOS but user-initiated unstaking presents the same issue as user-removing rows from a contract, we have no way of allowing dapps to maintain consistency in that model.

Also a callback when row is deleted for dapps would help

in my previous comment I tried to explain why no dapp could actually depend on this signaling therefore the reality is that its utility drops to nothing. What good would total_tokens be if there was no way to guarantee that it was consistent. The burden of establishing data-consistency given the fact that we can not guarantee strongly ordered at-least-one delivery for events like this is too high.