-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
raise ValidationError if ether is sent to non payable functions #1017
Conversation
web3/contract.py
Outdated
@@ -432,6 +433,15 @@ def estimateGas(self, transaction=None): | |||
"Please ensure that this contract instance has an address." | |||
) | |||
|
|||
if 'value' in estimate_transaction: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of duplicating this logic in multiple places, I think all of this code can go in the validation middleware.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Looks like this can be a def validate_payable(transaction, abi)
to consolidate the logic. Other than that, the approach is fine 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, a helper function would do.
The problem with the middleware approach is that the abi
will not be available there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@voith @carver I have consolidated the logic as discussed and decided to keep I was wondering if tests are needed for this change and if yes, I need some direction on how I should go about them. I tried to look up tests inside |
@ankit-maverick yes, we'll want to have tests added for this. These should probably be done with a new contract as to not conflate any of the existing testing contracts. Something simple like contract PayableTester {
bool public wasCalled;
function doPayableCall() public payable {
wasCalled = true;
}
function doNoValueCall() public {
wasCalled = true;
}
} For testing those, you can do something simple like:
These tests should be done for all of |
@ankit-maverick let me know if you need help getting the tests green. |
@pipermerriam Sure. I will finish this off this weekend and ask here when I need help. |
@pipermerriam One issue I faced last weekend with these tests is understanding the difference between 2 runs of the same tests
What's difference between both of them? Edit : Grammar |
There is an old API style for interacting with contracts that looks like:
(ie~ function last) versus the new style of:
(ie~ function first) The test failures imply that the changes work with the new style, but not the old style, API. If you're curious, here is how the tests are set up: web3.py/tests/core/contracts/conftest.py Lines 547 to 553 in 9800597
|
@carver @pipermerriam I have finished tests for Edit : It doesn't fail in the way it is currently failing when I pass any other parameter like |
tests/core/contracts/conftest.py
Outdated
|
||
function doPayableCall() public payable { | ||
wasCalled = true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we're never really testing doPayableCall
here. That's maybe fine (since it should be covered by other tests), but let's remove it from the contract and ABI, then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I will remove it.
payable_tester_contract, | ||
estimateGas): | ||
with pytest.raises(ValidationError): | ||
estimateGas(contract=payable_tester_contract, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would focus on why this is failing. Many of the other functions will fail if this fails, because calls like buildTransaction
will call estimateGas
under the hood, if no gas limit is supplied (which is common).
Can you reproduce outside of testing, with payable_tester_contract.estimateGas(dict(value=1)).doNoValueCall()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could reproduce it. Will dig deeper into it.
>>> contract_instance.estimateGas(dict(value=1)).doNoValueCall()
API Exception: {'type': 'TransactionFailed', 'args': (), 'message': ''}
Traceback (most recent call last):
File "/Users/ankit/web3.py/venv/lib/python3.7/site-packages/jsonrpc/manager.py", line 112, in _get_responses
File "/Users/ankit/web3.py/venv/lib/python3.7/site-packages/testrpc/server.py", line 34, in inner
File "/Users/ankit/web3.py/venv/lib/python3.7/site-packages/testrpc/rpc.py", line 138, in eth_estimateGas
File "/Users/ankit/web3.py/venv/lib/python3.7/site-packages/testrpc/client/client.py", line 253, in estimate_gas
File "/Users/ankit/web3.py/venv/lib/python3.7/site-packages/testrpc/client/client.py", line 263, in send_transaction
File "/Users/ankit/web3.py/venv/lib/python3.7/site-packages/testrpc/client/client.py", line 58, in inner
File "/Users/ankit/web3.py/venv/lib/python3.7/site-packages/testrpc/client/utils.py", line 104, in inner
File "/Users/ankit/web3.py/venv/lib/python3.7/site-packages/testrpc/client/client.py", line 225, in _send_transaction
File "/Users/ankit/web3.py/venv/lib/python3.7/site-packages/ethereum/tester.py", line 338, in send
File "/Users/ankit/web3.py/venv/lib/python3.7/site-packages/ethereum/tester.py", line 296, in _send
ethereum.tester.TransactionFailed
127.0.0.1 - - [13/Sep/2018 08:08:42] "POST / HTTP/1.1" 200 147
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/ankit/web3.py/web3/contract.py", line 1506, in estimate_gas_for_function
gas_estimate = web3.eth.estimateGas(estimate_transaction)
File "/Users/ankit/web3.py/web3/eth.py", line 304, in estimateGas
[transaction],
File "/Users/ankit/web3.py/web3/manager.py", line 112, in request_blocking
raise ValueError(response["error"])
ValueError: {'code': -32000, 'message': 'Server error', 'data': {'type': 'TransactionFailed', 'args': [], 'message': ''}}
@ankit-maverick So here's what's happening:
Notice here that the function Now for the old API you've added validation in the You should probably add the check inside Line 591 in d436b0a
Do the same for other methods like |
def FALLBACK_FUNCTION_CONTRACT(FALLBACK_FUNCTION_CODE, | ||
FALLBACK_FUNCTION_RUNTIME, | ||
FALLBACK_FUNCTION_ABI): | ||
def PAYABLE_TESTER_CONTRACT(PAYABLE_TESTER_CODE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @njgheorghita I was looking at this stuff and it might be a good test-bed for seeing how py-ethpm and maybe pytest-ethereum
could clean up these test fixtures.
@voith Thanks a ton. I have moved |
912b9f1
to
deb5806
Compare
@carver @pipermerriam @voith Need your final review on this PR. |
web3/contract.py
Outdated
|
||
def get_function_abi_from_contract_abi(contract_abi, function_name): | ||
for abi in contract_abi: | ||
if abi['name'] == function_name: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A contract can have multiple functions with the same name but different signatures.
eg.
contract FunctionOverloadingContract {
function someMethod(int i) public {
// do something
}
function someMethod(string s) public {
// do something
}
}
get_function_abi_from_contract_abi
might return the wrong abi in such a case.
Have a look find_matching_fn_abi
to understand more.
This code makes me think that the validation should probably be moved to methods like call_contract_function
, transact_with_contract_function
, build_transaction_for_function
, estimate_gas_for_function
. I have not done a complete analysis, but just throwing an idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@voith This idea makes more sense than the approach I have taken. I could get rid of get_function_abi_from_contract_abi
and better use find_matching_fn_abi
& check validate_payable
inside lower level base functions call_contract_function
, transact_with_contract_function
, build_transaction_for_function
, estimate_gas_for_function
which are being called from both new and old style APIs.
@carver @pipermerriam @voith Please review. If all looks good, I will squash the commits before we merge this. |
web3/contract.py
Outdated
if fn_abi is None: | ||
fn_abi = find_matching_fn_abi(contract_abi, function_name, args, kwargs) | ||
|
||
validate_payable(transaction, fn_abi) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like all four of these copy/pasted chunks come just before a prepare_transaction()
. Can the lines move inside prepare_transaction()
, so the repeats can be deleted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, don't know how I missed it. Pushing the change now.
Great, I think this is ready for your squash and we can get it merged @ankit-maverick ! |
eba38fb
to
7d83c0e
Compare
@carver @pipermerriam Ready to go in. |
@carver @voith @pipermerriam Thanks a lot. This was my first contribution to anything crypto and I learned a lot of concepts while working through this PR(although I assumed it to be a very straightforward PR in the beginning). |
Thanks for the contribution @ankit-maverick ! |
Will this make it to the next release (4.7.2)? |
Ah, this went into the master branch which is on v5, it would need to be backported and merged into v4 to go into 4.7.2. |
What was wrong?
Related to Issue #902
How was it fixed?
Raised
ValidationError
incall
,transact
,estimateGas
,buildTransaction
if ether is sent to functions that are not payable.TODOs:
Cute Animal Picture