Skip to content

Commit

Permalink
Merge pull request #402 from DavidKnott/re-entrancy-protection
Browse files Browse the repository at this point in the history
Add staticcall opcode for constant external function calls
  • Loading branch information
DavidKnott authored Nov 14, 2017
2 parents 14ed6cd + 5ffdef8 commit 974c62e
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 14 deletions.
3 changes: 2 additions & 1 deletion tests/examples/company/test_company.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

from ethereum.tools import tester as t
from ethereum import utils
from viper import compiler

from tests.setup_transaction_tests import assert_tx_failed
from viper import compiler

@pytest.fixture
def tester():
Expand Down
33 changes: 32 additions & 1 deletion tests/parser/features/test_external_contract_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def get_array(arg1: address) -> bytes <= 3:
assert c2.get_array(c.address) == b'dog'


def test_external_contract_call__state_change():
def test_external_contract_call_state_change():
contract_1 = """
lucky: public(num)
Expand All @@ -101,6 +101,37 @@ def set_lucky(arg1: address, arg2: num):
print('Successfully executed an external contract call state change')


def test_constant_external_contract_call_cannot_change_state(assert_tx_failed):
contract_1 = """
lucky: public(num)
def set_lucky(_lucky: num) -> num:
self.lucky = _lucky
return _lucky
"""

lucky_number = 7
c = get_contract(contract_1)

contract_2 = """
class Foo():
def set_lucky(_lucky: num) -> num: pass
@constant
def set_lucky_expr(arg1: address, arg2: num):
Foo(arg1).set_lucky(arg2)
@constant
def set_lucky_stmt(arg1: address, arg2: num) -> num:
return Foo(arg1).set_lucky(arg2)
"""
c2 = get_contract(contract_2)

assert_tx_failed(lambda: c2.set_lucky_expr(c.address, lucky_number))
assert_tx_failed(lambda: c2.set_lucky_stmt(c.address, lucky_number))
print('Successfully tested an constant external contract call attempted state change')


def test_external_contract_can_be_changed_based_on_address():
contract_1 = """
lucky: public(num)
Expand Down
1 change: 1 addition & 0 deletions viper/opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
'INVALID': [0xfe, 0, 0, 0],
'SUICIDE': [0xff, 1, 0, 5000],
'SELFDESTRUCT': [0xff, 1, 0, 25000],
'STATICCALL': [0xfa, 6, 1, 40],
}

pseudo_opcodes = {
Expand Down
29 changes: 17 additions & 12 deletions viper/parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,11 +461,13 @@ def external_contract_call_stmt(stmt, context):
sig = context.sigs[contract_name][method_name]
contract_address = parse_expr(stmt.func.value.args[0], context)
inargs, inargsize = pack_arguments(sig, [parse_expr(arg, context) for arg in stmt.args], context)
o = LLLnode.from_list(['seq',
['assert', ['extcodesize', ['mload', contract_address]]],
['assert', ['ne', 'address', ['mload', contract_address]]],
['assert', ['call', ['gas'], ['mload', contract_address], 0, inargs, inargsize, 0, 0]]],
typ=None, location='memory', pos=getpos(stmt))
sub = ['seq', ['assert', ['extcodesize', ['mload', contract_address]]],
['assert', ['ne', 'address', ['mload', contract_address]]]]
if context.is_constant:
sub.append(['assert', ['staticcall', 'gas', ['mload', contract_address], inargs, inargsize, 0, 0]])
else:
sub.append(['assert', ['call', 'gas', ['mload', contract_address], 0, inargs, inargsize, 0, 0]])
o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(stmt))
return o


Expand All @@ -487,13 +489,16 @@ def external_contract_call_expr(expr, context):
returner = output_placeholder + 32
else:
raise TypeMismatchException("Invalid output type: %r" % sig.output_type, expr)
o = LLLnode.from_list(['seq',
['assert', ['extcodesize', ['mload', contract_address]]],
['assert', ['ne', 'address', ['mload', contract_address]]],
['assert', ['call', ['gas'], ['mload', contract_address], 0,
inargs, inargsize,
output_placeholder, get_size_of_type(sig.output_type) * 32]],
returner], typ=sig.output_type, location='memory', pos=getpos(expr))
sub = ['seq', ['assert', ['extcodesize', ['mload', contract_address]]],
['assert', ['ne', 'address', ['mload', contract_address]]]]
if context.is_constant:
sub.append(['assert', ['staticcall', 'gas', ['mload', contract_address], inargs, inargsize,
output_placeholder, get_size_of_type(sig.output_type) * 32]])
else:
sub.append(['assert', ['call', 'gas', ['mload', contract_address], 0, inargs, inargsize,
output_placeholder, get_size_of_type(sig.output_type) * 32]])
sub.extend([0, returner])
o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(expr))
return o


Expand Down

0 comments on commit 974c62e

Please sign in to comment.