-
Notifications
You must be signed in to change notification settings - Fork 11.8k
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
Method decreaseApproval
in unsafe
#437
Comments
Hi @mikhail-vladimirov, thanks for reporting! (And thanks for the overall thorough review.) I thought about this and I'm not sure I agree with either the risk or the proposed mitigation. At point 4, once Alice sees her call succeeded, she would be able to see any Furthermore, suppose we changed |
If Bob had transferred Alice's tokens to Carol, not to himself. it would be very non-trivial to know that this transfer was done by Bob, not Carol.
Bob anyway had a chance to get all the allowed tokens, and he had this ability for a long time, nothing bad if he will be able to do this for little more time. Bad thing is not that Bob is able to get tokens, this is actually normal, because Alice herself allowed him to do this. Bad thing is that after changing the allowance, Alice does not know how much of the original allowance was actually used by Bob, and thus does not know how much tokens she owes Bob, or Bob owes her; so Bob may actually cheat Alice by pretending that he didn't use any allowance (thus Alice still owes some tokens to Bob) while he actually did use it. So safe |
This is a different problem that I completely agree with, which is the lack of a
Suppose "Bob" is a smart contract that is programmed to make a transfer with a certain frequency and you want to stop it immediatey. I agree that I share some of the concerns but I think most of the risk can be mitigated by logging how much an approved spender has transferred. Do you agree? I'm open to hearing more opinions though. Have you reported this elsewhere? |
There are absolutely no problems with Bob being able to transfer as many tokens as Alice approved to him until Alice revoked the approval. This is how token contracts are supposed to work. Regardless of what Bob does, once Alice approved some tokens to Bob, she approved, and it is her full responsibility to make sure Bob is a good guy before approving anything to him. The real problems with ERC20 are:
In current form, |
Hi Guys, I've enjoyed reading the discussion. Thx, for pointing out the vulnerability @mikhail-vladimirov! Currently we have a soft condition requiring that the deduction is lower than allowance or the allowance is reduced to zero. How about making it stronger and requesting that the current allowance is equal to a state known by transaction sender.
Using this approach we will make it sure that the operation exactly satisfies the intentions of the sender and the current state of the contract matches state know by the sender. It will also make the whole process of allowance update atomic and prevent race conditions. Actually, the proposed design will be a form of Compare and Swap pattern which is widely used in concurrency programming. |
This should work, but in this case you probably need to remove "decrease" from the method name and make it able to change allowance in either direction. |
I like @jakub-wojciechowski's proposal because it does make the semantics quite apparent from the function signature. I feel like I've seen the Compare and Swap function proposed before as a solution to the |
I'm really glad that you liked it. I'm certainly not the one to take credit for the Compare and Swap pattern as it's one of the most widely used lock-free synchronisation strategy and probably older than me :) I agree with @mikhail-vladimirov that it'd be better to merge both |
Here is an example of compare and set approve function in ERC20 token smart contract: https://github.com/abdk-consulting/icos-token-contract/blob/master/src/sol/ICOSToken.sol#L106 |
@mikhail-vladimirov that example is not ERC20 compliant. This should also be taken into account #438 |
It is compliant with original ERC20. It does not strictly comply with EIP-20 though, because it was created before EIP-20 was finalized. |
it's been a while that |
Oh, now I see what did you mean when said that contract I referred to is not ERC20 compliant. Of cause that contract has standard two-argument |
I'd go ahead with a CAS function. What should we name it? It's technically possible to call it |
since safeApprove is taken, maybe secureApprove? |
Interesting topic. I agree that I don't feel it is complicated to understand what happens to tokens, and in any case I believe that any possible difficulty in understanding what has happened in the past, it is surely to be preferred to the uncertainty of what will happen in the future. When I send a TX, I pay for that, and I want an effect. On the contrary, with implementation of CAS the spender has the power to completely stop the effect of my TX. Moreover, if the check is implemented with The CAS approach is an interesting solution in concurrency programming but in the execution of Ethereum transactions there's no concurrency at all, so it think that pattern does not fit very well. |
The problem here is with 'check-balance' step. If your account is quite busy, tokens are constantly going in and out and you approved transfers from it to many people, then your balance is constantly changing even without your intervention and checking balance will tell you nothing about whether the one whose allowance you just tried to revoke managed to use it in the very last moment or not; but you need to know this for sure to perform the next step: think-how-much-allowance-you-want-to-set. |
Correct, but the difficulty to understand what is happening on your balance is not related to your intention to modify the allowance at all, that problem is there in any case. To monitor your token balance you need to establish an app that reads all blocks and transactions related to your token contract, and then you can understand who moved the tokens looking at the sender of the transacion. So, let's say you have implemented an app that monitor your tokens, do you still need a CAS approach? If the answer is no, as I think, then I disagree to define the CAS approach as a best practice for setting allowance. |
This relation exists and you described it in the schema you suggested as the safest way to change allowance: set-0,check-balance,think-how-much-allowance-you-want-to-set,set-allowance-again Difficulty to understand what is happening on your balance directly affects you ability to perform check-balance step and thus directly affects your ability to execute the whole procedure. |
The answer is yes, you still do. Because the balance could still change between the moment you check your balance on your app and the moment your transaction to set the new allowance is recorded. |
I'm not convinced yet :) I'm thinking about this workflow:
At step 3, if I send a
So, why |
If your account is busy, tokens are constantly going in and out, and many people are allowed to transfer from it, then monitoring your tokens will not help because your token balance will be changing both ways all the time even without Bob's interventions.
Assuming that Bob is a contract (reasonable assumption, because |
Yes, again, I already agreed with you on this point: monitoring tokens movements is difficult (not impossible). This monitoring is difficult in any case, and using Another point is this: if you implement a CAS approach inside the |
CAS |
@Neurone that depends on how you use approve. |
@mikhail-vladimirov Sorry but I can't :-) basically because I think the CAS approach in the
I simply don't think is a good approach blocking a TX that want to modify the state, just because I can't easily understand that state. |
|
@SylTi once approved, no subsequent TX can guarantee to block the other party to widthdraw that amount. If CAS approach is implemented, you are enslaving the result of your TX to the behaviour of someone else. You can't control that behaviour, so you don't know if TX will be executed or it will throw an exception. I believe that is not a good approach having this kind of uncertainty when calling a smart contract's function. @mikhail-vladimirov @SylTi Clearly we have different visions about smart contract design. It's not a problem for me, let's have a separated |
We've got a very interesting discussion in here. There is no silver bullet to solve all of the problems with the approve method. Imho, the best solution is to have 2 separate methods:
It will be up to the users whether they want their transaction to be effective immediately with the risk of inconsistency with their expectations or to perform the update only under strict preconditions. It slightly reminds me the battle between optimistic and pessimistic locking in relational databases. |
It is not possible via standard tools and Web3 API to get return value of contact call executed non-locally (i.e. mined as a transaction), so this will not help a lot. This probably will be changed in future (ethereum/EIPs#658), but as for now, this is not a solution from my point of view. |
Good news. EIP685 was merged in August and it was released few days ago with Geth 1.7. |
Thanks for the discussion guys, after some consideration we've decided to go with the original proposal by @3sGgpQ8H. |
Method
decreaseApproval
inStandardToken.sol
is unsafe. Here is the scenario.approve
orincreaseApproval
method and transaction is executed successfullydecreaseApproval
method to decrease by 100 the number of her tokens Bob is allowed to transfer and transaction is executed successfully and proper Approval event was loggeddecreaseApproval
call was executed successfully, then Bob didn't manage to transfer any of her tokens before the allowance was decreased, but this assumption is wrong.Actually, Bob may or may not had transferred Alice's tokens before allowance was decreased, and Alice has no easy way to know for sure whether Bob transferred her tokens or not
Method
decreaseApproval
should fail in case current allowance is lower than requested decrease.The text was updated successfully, but these errors were encountered: