Currently, call does not fail if the target contract does not exist. The idea is that it is a low-level thing.
People might still not think about that.
On the other hand, this makes it impossible to use call for sending ether to external accounts.
On the third hand, you should use transfer for that anyway.
Also note that extcodesize is 700 gas.
1) If the callee was a contract, it would have to expose some part of its ABI as payable for it to be able to receive ether. That is, contracts need to explicitly declare that they can receive ether.
Now, would you impose the same requirement on a regular account? What purposes does an account have other than representing an identity and receiving funds? So I don't see a reason for them to have to explicitly declare such an ability.
2) What benefits do you see in adding this restriction?
Is there a valid use case where you may want to send some data along with the ETH to an external account, to later retrieve that data from an app off-chain, and act based on it? (similar to the the data parameter present on the transfer methods of some token standards). If so, I'd not add the extcodesize check.
That said, an example of such use case doesn't come to mind now.
My gut feeling is that call should stay a low-level function, without additional checks. If this isn't desired I'd consider removing it in favor of just inline assembly.
I agree with @frangio, but that I don't think it should be removed. Dealing with with inline assembly in program analysis tools is much harder, so IMO the least it's used the better.
@spalladino I've seen extra data sent to a contract's default function for tracking purpose in ICOs. I wouldn't be surprised if this technique is also used for external accounts.
@ajsantander note that this is not about sending ether, but just about sending data. The problem is that if you send data to an account that does not have code, it will be successful, even though you would expect some code to execute.
I agree with keeping things simple, but we check for extcodesize with regular function calls and thus people might expect the same thing to happen for low-level calls. Note that this would also provide some kind of protection from "library" selfdestructs (note that libraries cannot selfdestruct anymore, you have to use the contract workaround that was actually also used in the two library-related bugs) combined with low-level delegatecall.
The main question would be which use-case is more edge-casy: deliberately sending data to non-contract accounts or assuming that call executed code just because it did not fail.
One resolution of this issue could also be: "call never checked for extcodesize so it should not start doing so now", especially if we cannot find strong arguments for either side.
I think with introducing .iscontract() on the address type would negate the need for this.
Agreed on todays meeting that this replaced #4910. The current behaviour is simpler and it isn't fully clear which option is better (having the check or not having the check), but by introducing a helper (as of #4910) the option is put in the hands of the user.
@ajsantander @frangio @spalladino @alcuadrado please comment if you would like to keep this issue open and discussed further.
Most helpful comment
Agreed on todays meeting that this replaced #4910. The current behaviour is simpler and it isn't fully clear which option is better (having the check or not having the check), but by introducing a helper (as of #4910) the option is put in the hands of the user.
@ajsantander @frangio @spalladino @alcuadrado please comment if you would like to keep this issue open and discussed further.