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

BREAKING: Add payable modifier #665

Merged
merged 11 commits into from
Sep 6, 2016
Merged

BREAKING: Add payable modifier #665

merged 11 commits into from
Sep 6, 2016

Conversation

axic
Copy link
Member

@axic axic commented Jun 18, 2016

Partially implements #500 and #563. Comments welcome.

Fixes #602.

todo:

  • library function should not be payable
  • contract is payable should create payable fallback function (and error if there already is one)
  • calling a non-payable function with value set should report an error
  • accessor functions should not be able to receive ether (-> callvalue check should probably move to dispatcher)
  • internal functions should not allow payable modifier
  • overwriting a base contract function should not change payable
  • force constant and payable to be mutually exclusive

@axic axic changed the title Add valueAccepted modifier Add valueAccepted modifier [WIP] Jun 20, 2016
@chriseth
Copy link
Contributor

This severely breaks backwards compatibility and has to be discussed thoroughly.

I think it would be better to not have this as a bulit-in compiler feature. If we want to use something like that for analysis and error reporting purposes, the modifier could be added to a standard library. Having said that, I'm not so keen on keeping the hardcoded standard libraries.

@redsquirrel
Copy link
Contributor

@chriseth Any specific plans for the standard libraries?

@axic
Copy link
Member Author

axic commented Jun 24, 2016

@chriseth I think the reason it should be built in is to support it in the interface JSON. Unless you want to introduce a feature whereby flags can be added to the interface JSON via modifiers or other language features.

@axic
Copy link
Member Author

axic commented Jun 24, 2016

Alternative approach to not break backwards compatibility to have a rejectValue flag in the interface JSON. The language could have either ways, but in that case probably it is preferable to mirror the interface JSON, i.e. have a rejectValue modifier.

I do think that the idea of making backwards incompatible changes at this stage, in a coordinated effort, should not be withdrawn.

@chriseth
Copy link
Contributor

chriseth commented Jul 8, 2016

Ok, I agree that it might be handy to know whether a function accepts ether especially with auto-generated contract interfaces in mind.

I don't especially like the name of the modifier - value is a bit too generic, although I know that we have msg.value. Also I would prefer the active form acceptsValue.

For the sake of usability, a default of rejecting ether transfer should of course be preferred. It gets more complicated at the point where we have public functions which are helper functions and are called from functions that accept value transfer - then these would throw.

@axic
Copy link
Member Author

axic commented Jul 12, 2016

@chriseth the code currently has valueAccepted as a keyword and acceptvalue in the ABI JSON.

Would you rather go for acceptsValue and acceptsvalue?

Also this PR doesn't implement the actual code to reject such transactions, so I do not see any backward compatibility break. Rejection should be implemented of course and I would prefer to reject if it value as given to a non-acceptsValue method.

@chriseth
Copy link
Contributor

chriseth commented Jul 18, 2016

I think I would prefer acceptsEther but I'm open for suggestions; and please implement the full thing in one PR.

@axic
Copy link
Member Author

axic commented Aug 1, 2016

@chriseth I would probably go with acceptsValue or acceptsEther.

Regarding the latter I'm not sure how the system will look like once Ether becomes a token contract:

  1. Will value passing be removed from normal transactions and it has to be assigned as usual via the token interface?
  2. Or will the transaction have the token type (i.e. token contract address) passed along the value?

In case 2), acceptsEther is misleading.

@chriseth
Copy link
Contributor

chriseth commented Aug 1, 2016

I don't think we will go with 2. If we do, we can still add a new modifier acceptsToken(Token _token)

@axic
Copy link
Member Author

axic commented Aug 2, 2016

Agreed with @chriseth that we're going for the keyword payable.

@axic axic changed the title Add valueAccepted modifier [WIP] Add payable modifier [WIP] Aug 2, 2016
@chriseth
Copy link
Contributor

chriseth commented Aug 5, 2016

Please check (as in write a test) that this also applies to accessor functions!

@@ -426,6 +410,13 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)

m_context.startFunction(_function);

// Reject transaction if value is not accepted, but was received
if (!_function.isPayable())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question is, should it be in visit(FunctionDefiniton) or appendFunctionSelector?

Copy link
Contributor

@chriseth chriseth Aug 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

visit(FunctionDefinition) is also used for internal functions, so it should be in the selector or the calldata unpacker.

@axic
Copy link
Member Author

axic commented Aug 5, 2016

@chriseth This works now, but a lot tests need to be fixed to include the keyword.

@axic
Copy link
Member Author

axic commented Aug 8, 2016

It may be a good time to review some of those EndToEnd tests whether they actually require a value to be sent at them. Some looked like they may only have it as an oversight.

@chriseth chriseth added this to the 2-breaking-safety-changes milestone Aug 8, 2016
@@ -2045,7 +2045,7 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses)
}
contract test {
helper h;
function test() { h = new helper(); h.send(5); }
function test() payable { h = new helper(); h.send(5); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you actually handle the constructor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I've only added the keyword to the tests where it was failing without the keyword and I've reviewed that the code actually needs it.

@chriseth
Copy link
Contributor

chriseth commented Aug 8, 2016

I think you added payable to a lot of end to end tests where this is not needed.

@axic axic mentioned this pull request Aug 8, 2016
@chriseth chriseth changed the title Add payable modifier [WIP] BREAKING: Add payable modifier Aug 11, 2016
@chriseth
Copy link
Contributor

chriseth commented Aug 25, 2016

We could add another feature: contract a is payable { ... } results in contract a receiving an empty fallback function and errors if it already has a fallback function.

@ethernomad
Copy link
Contributor

Will this mean that getters generated for public variables will throw if they are called with a value transfer?

@chriseth
Copy link
Contributor

@ethernomad yes

@axic
Copy link
Member Author

axic commented Aug 31, 2016

@chriseth I think constant and payable modifiers should be mutually exclusive.

constant means that the state cannot be modified, while receiving value is modifying the balance of the state. (Albeit it is not done by the code running on EVM.)

@chriseth
Copy link
Contributor

chriseth commented Sep 1, 2016

@axic ok, that makes sense.

@chriseth
Copy link
Contributor

chriseth commented Sep 5, 2016

Removed the contract A is payable feature again as it is probably of limited use. We can still add it without breaking backwards-compatibility.

char const* sourceCode = R"(
contract C {
modifier tryCircumvent {
if (false) _ // avoid the function, we should still not accept ether
Copy link
Member

@pirapira pirapira Sep 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now a semicolon after _ is necessary after #1005.

if (!_function.isConstructor() && !_function.name().empty() && !_function.isPartOfExternalInterface())
typeError(_function.location(), "Internal functions cannot be payable.");
}
if (_function.isPayable() && _function.isDeclaredConst())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be merged into the above if.

@axic
Copy link
Member Author

axic commented Sep 6, 2016

Two tests needed:

  • ABI test for payable fallback
  • payable private method should be disallow (similar to how payable internal is)

@chriseth
Copy link
Contributor

chriseth commented Sep 6, 2016

@axic please review again

@axic
Copy link
Member Author

axic commented Sep 6, 2016

@chriseth looks good

@chriseth chriseth merged commit f687635 into ethereum:develop Sep 6, 2016
@axic axic deleted the feature/accept-ether branch October 25, 2016 11:43
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

Successfully merging this pull request may close these issues.

5 participants