Repository: vyperlang/vyper

Issue:
Potentially insufficient gas stipend for precompiled contract calls
### Version Information

* vyper Version (output of `vyper --version`): `0.1.0b13+commit.375fb27`

(confirmed the same behavior at `0.1.0b14+commit.49b201c` as well.)

### What's your issue about?

Vyper utilizes the ID precompiled contract call for doing memcpy-like operations. That is, the compiled bytecode contains the opcode pattern `CALL GASCAP 4 0 SRC SIZE DEST SIZE` to copy the local memory region from SRC to DEST.

The problem is that the allocated gas stipend `GASCAP` for the call in the bytecode is constant-bounded, (e.g., 21 when SIZE == 32), which may not be sufficient in a future hardfork that modifies the gas schedule. Thus this may result in that any such future hardfork may be significantly delayed or long-lasting contracts (such as the deposit contract) may need to be redeployed.

Is there a specific reason to have this tight gas cap bound?

BTW, this behavior doesn't seem to be consistent. For example, when it calls to the SHA256 precompiled contract, it simply allocates all available gas for the call stipend.

### How can it be fixed?

Allocate all available gas (i.e., using the `GAS` opcode) when calling to precompiled contracts.



Discussion:
Question:  In case that this is indeed a bug, is it possible to release a hotfix version that fixes this issue on top of the beta 13 (not the beta 14)? That will significantly help re-run the deposit bytecode verification against the fixed bytecode, without going through all the changes made since the beta 13 release.
This should just be changed to use `gas` opcode and not a pre-calculated value (like sha256 does it).
https://github.com/vyperlang/vyper/blob/master/vyper/parser/parser_utils.py#L120
I think this needs to be fixed as well:
https://github.com/vyperlang/vyper/blob/master/vyper/parser/parser_utils.py#L168
Indeed, there are other places whether the same thing happens, e.g.,
https://github.com/vyperlang/vyper/blob/master/vyper/functions/functions.py#L452
https://github.com/vyperlang/vyper/blob/master/vyper/functions/functions.py#L473
https://github.com/vyperlang/vyper/blob/master/vyper/functions/functions.py#L490
https://github.com/vyperlang/vyper/blob/master/vyper/functions/functions.py#L941-L942

Not sure if there are more, though.
Yes indeed; thanks.
I want second the request to hotfix this as a release on b13 in addition to incorporating it for further releases. Our formal verification efforts on the eth2 deposit contract are very near complete and bumping to b14+ to fix this bug would majorly set us back.

Thank you!
It should be possible to have a fix for this be easily based on b13. We'd like to avoid maintaining a fork of b13, but obviously want to help.

Resolving patch:
diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py
--- a/vyper/functions/functions.py
+++ b/vyper/functions/functions.py
@@ -449,7 +449,7 @@ def ecrecover(expr, args, kwargs, context):
         ['mstore', ['add', placeholder_node, 32], args[1]],
         ['mstore', ['add', placeholder_node, 64], args[2]],
         ['mstore', ['add', placeholder_node, 96], args[3]],
-        ['pop', ['call', 3000, 1, 0, placeholder_node, 128, MemoryPositions.FREE_VAR_SPACE, 32]],
+        ['pop', ['call', ['gas'], 1, 0, placeholder_node, 128, MemoryPositions.FREE_VAR_SPACE, 32]],
         ['mload', MemoryPositions.FREE_VAR_SPACE],
     ], typ=BaseType('address'), pos=getpos(expr))
 
@@ -470,7 +470,7 @@ def ecadd(expr, args, kwargs, context):
         ['mstore', ['add', placeholder_node, 32], avo(args[0], 1, pos)],
         ['mstore', ['add', placeholder_node, 64], avo(args[1], 0, pos)],
         ['mstore', ['add', placeholder_node, 96], avo(args[1], 1, pos)],
-        ['assert', ['call', 500, 6, 0, placeholder_node, 128, placeholder_node, 64]],
+        ['assert', ['call', ['gas'], 6, 0, placeholder_node, 128, placeholder_node, 64]],
         placeholder_node,
     ], typ=ListType(BaseType('uint256'), 2), pos=getpos(expr), location='memory')
     return o
@@ -487,7 +487,7 @@ def ecmul(expr, args, kwargs, context):
         ['mstore', placeholder_node, avo(args[0], 0, pos)],
         ['mstore', ['add', placeholder_node, 32], avo(args[0], 1, pos)],
         ['mstore', ['add', placeholder_node, 64], args[1]],
-        ['assert', ['call', 40000, 7, 0, placeholder_node, 96, placeholder_node, 64]],
+        ['assert', ['call', ['gas'], 7, 0, placeholder_node, 96, placeholder_node, 64]],
         placeholder_node,
     ], typ=ListType(BaseType('uint256'), 2), pos=pos, location='memory')
     return o
diff --git a/vyper/parser/parser_utils.py b/vyper/parser/parser_utils.py
--- a/vyper/parser/parser_utils.py
+++ b/vyper/parser/parser_utils.py
@@ -117,7 +117,7 @@ def make_byte_array_copier(destination, source, pos=None):
         o = LLLnode.from_list([
             'with', '_source', source, [
                 'with', '_sz', ['add', 32, ['mload', '_source']], [
-                    'assert', ['call', ['add', 18, ['div', '_sz', 10]], 4, 0, '_source', '_sz', destination, '_sz']]]],  # noqa: E501
+                    'assert', ['call', ['gas'], 4, 0, '_source', '_sz', destination, '_sz']]]],  # noqa: E501
             typ=None, add_gas_estimate=gas_calculation, annotation='Memory copy'
         )
         return o
@@ -165,7 +165,7 @@ def make_byte_slice_copier(destination, source, length, max_length, pos=None):
             'with', '_l', max_length,
             [
                 'pop',
-                ['call', 18 + max_length // 10, 4, 0, source, '_l', destination, '_l']
+                ['call', ['gas'], 4, 0, source, '_l', destination, '_l']
             ]
         ], typ=None, annotation=f'copy byte slice dest: {str(destination)}')
     # Copy over data


Test patch: