Repository: vyperlang/vyper

Issue:
Add SafeMath to `num` type
### What's your issue about?
`num` is currently able to overflow in Viper`
The following code works: 
```
def test_num():
    code = """
@public
def num_sub() -> num:
    return 1-2**256
"""
    c = get_contract(code)
    assert c.num_sub() == 1
```
### How can it be fixed?
Added SafeMath checks to `num` arithmetic.

Fill this in if you know how to fix it.
In `expr.py` in function `def arithmetic(self):` Add in SafeMath LLL
#### Cute Animal Picture
![image](https://user-images.githubusercontent.com/17552858/32912263-b5eb28f2-cacb-11e7-8386-1007413e367b.png)




Discussion:
Safemath is a pretty easy thing, basically just assertions that the math won't overflow before you do the math e.g. `assert a-b == -(b-a)` for `a-b` (or something like that). We can probably adopt the solidity safe math library pretty easily.

It's more a strategic issue to me. Do we want to enforce **all** math to be safe math, therefore adding extra assertion code and costing extra gas? I believe for Viper that answer is yes, but I wanted to clarify this point.
Also this is adding runtime errors that don't have to do with out of gas errors, and therefore hard to track (without an error log)

Speaking of error logs, what do you think about a native Viper error log on all contracts?
Related to error logs, `assert condition` could become a macro for:
```python
if cond:
     logs.Error('AssertionError', ...)
     throw
```
@fubuloubu I think everything being overflow / underflow is worth the extra gas, given that so far we've been prioritizing trying to prioritize safety everywhere so far.  
@DavidKnott @fubuloubu I believe this has been implemented?
I think so

In the future we can run optimizations that can formally verify SafeMath as unnecessary and remove it for gas savings. Wayyyy down the line though.
Re-opening as the above test case still fails. 
`2**256` shouldn't work though... It's above the uint256 limit
Safety everywhere indeed!
Given this catastrophic integer overflow issue in a Solidity token contract https://medium.com/@peckshield/alert-new-batchoverflow-bug-in-multiple-erc20-smart-contracts-cve-2018-10299-511067db6536 and the fact that developers can write code outside of the help provided by SafeMath and other analysis tools it makes sense to enforce overflow protection at the compiler level, yeah?
Yip exactly! Vyper's main goal is to make it maximally difficult to introduce insecure code 😉 and to focus on auditable / readable code.
:100: 

Resolving patch:
diff --git a/vyper/parser/expr.py b/vyper/parser/expr.py
--- a/vyper/parser/expr.py
+++ b/vyper/parser/expr.py
@@ -259,6 +259,8 @@ def arithmetic(self):
                 val = left.value / right.value
             elif isinstance(self.expr.op, ast.Mod):
                 val = left.value % right.value
+            elif isinstance(self.expr.op, ast.Pow):
+                val = left.value ** right.value
             else:
                 raise ParserException('Unsupported literal operator: %s' % str(type(self.expr.op)), self.expr)
 


Test patch:
diff --git a/tests/parser/types/numbers/test_int128.py b/tests/parser/types/numbers/test_int128.py
--- a/tests/parser/types/numbers/test_int128.py
+++ b/tests/parser/types/numbers/test_int128.py
@@ -1,4 +1,4 @@
-from vyper.exceptions import TypeMismatchException
+from vyper.exceptions import TypeMismatchException, InvalidLiteralException
 from decimal import Decimal
 
 
@@ -131,3 +131,80 @@ def foo():
     c = a ** b
 """
     assert_compile_failed(lambda: get_contract_with_gas_estimation(code), TypeMismatchException)
+
+
+def test_overflow_out_of_range(get_contract, assert_compile_failed):
+    code = """
+@public
+def num_sub() -> int128:
+    return 1-2**256
+    """
+
+    assert_compile_failed(lambda: get_contract(code), InvalidLiteralException)
+
+
+def test_overflow_add(get_contract, assert_tx_failed):
+    code = """
+@public
+def num_add(i: int128) -> int128:
+    return (2**127-1) + i
+    """
+    c = get_contract(code)
+
+    assert c.num_add(0) == 2**127 - 1
+    assert c.num_add(-1) == 2**127 - 2
+
+    assert_tx_failed(lambda: c.num_add(1))
+    assert_tx_failed(lambda: c.num_add(2))
+
+
+def test_overflow_add_vars(get_contract, assert_tx_failed):
+    code = """
+@public
+def num_add(a: int128, b: int128) -> int128:
+    return a + b
+    """
+    c = get_contract(code)
+
+    assert_tx_failed(lambda: c.num_add(2**127 - 1, 1))
+    assert_tx_failed(lambda: c.num_add(1, 2**127 - 1))
+
+
+def test_overflow_sub_vars(get_contract, assert_tx_failed):
+    code = """
+@public
+def num_sub(a: int128, b: int128) -> int128:
+    return a - b
+    """
+
+    c = get_contract(code)
+
+    assert c.num_sub(-2**127, -1) == (-2**127) + 1
+    assert_tx_failed(lambda: c.num_sub(-2**127, 1))
+
+
+def test_overflow_mul_vars(get_contract, assert_tx_failed):
+    code = """
+@public
+def num_mul(a: int128, b: int128) -> int128:
+    return a * b
+    """
+
+    c = get_contract(code)
+
+    assert c.num_mul(-2**127, 1) == -2**127
+    assert_tx_failed(lambda: c.num_mul(2**126, 2))
+
+
+def test_overflow_pow_vars(get_contract, assert_tx_failed):
+    code = """
+@public
+def num_pow(a: int128, b: int128) -> int128:
+    return a ** b
+    """
+
+    c = get_contract(code)
+
+    assert c.num_pow(-2, 127) == (-2**127)
+    assert c.num_pow(2, 126) == (2**126)
+    assert_tx_failed(lambda: c.num_pow(2**126, 2))