Repository: vyperlang/vyper

Issue:
Can't pass a literal to a `bytes32` variable?
### Version Information

* vyper Version: 0.1.0b4

### What's your issue about?

I could pass literals to `bytes[n]` types but couldn't to `bytes32` . 

```
@public
def test():
    # Compiled!
    # l: bytes[32] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xac\x58\xcd"

    # Compiled!
    # l: bytes[4] = "\x00\x00\x00\x00"

    # Failed    
    l: bytes32 = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xac\x58\xcd"
```

Error message (with `vyper-serve` ) is the below.

```
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 51879)
Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socketserver.py", line 647, in process_request_thread
    self.finish_request(request, client_address)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socketserver.py", line 357, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socketserver.py", line 717, in __init__
    self.handle()
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/server.py", line 426, in handle
    self.handle_one_request()
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/server.py", line 414, in handle_one_request
    method()
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/EGG-INFO/scripts/vyper-serve", line 66, in do_POST
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/EGG-INFO/scripts/vyper-serve", line 89, in _compile
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/compiler.py", line 44, in mk_full_signature
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/compiler.py", line 27, in gas_estimate
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/parser/parser.py", line 794, in parse_to_lll
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/parser/parser.py", line 224, in parse_tree_to_lll
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/parser/parser.py", line 172, in parse_other_functions
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/parser/parser.py", line 530, in parse_func
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/parser/parser.py", line 530, in <listcomp>
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/parser/parser.py", line 547, in parse_body
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/parser/parser.py", line 601, in parse_stmt
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/parser/stmt.py", line 76, in __init__
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/parser/stmt.py", line 150, in ann_assign
  File "/Users/ryuya.nakamura/work/vyper/venv/lib/python3.7/site-packages/vyper-0.1.0b4-py3.7.egg/vyper/parser/stmt.py", line 105, in _check_valid_assign
AttributeError: 'ByteArrayType' object has no attribute 'typ'
----------------------------------------
```

#### Cute Animal Picture

![image](https://user-images.githubusercontent.com/22876645/48686163-fb4a3d80-ebfd-11e8-9274-86965297ec47.png)




Discussion:
@nrryuya To add to this I'm not which parts of this are a bug with language implementation vs. bug in the language documentation. 

Passing the same `byte32` as a literal this way compiles fine:

```python
@public
def test():
    # Compiled!
    foo: bytes[32] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xac\x58\xcd"

    # Compiled!
    bar: bytes[4] = "\x00\x00\x00\x00"

    # Failed (though this may be expected)
    # l: bytes32 = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xac\x58\xcd"

    # Compiled!
    foobar: bytes32 = 0x0000000000000000000000000000000000000000000000000000000080ac58cd
```

However, the docs do list the default `bytes32` value as being `'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'`, which makes it seem like one should use the syntax you used.

Also, my intuition would be to expect the syntax to be the same for `bytes` and `bytes32`. It would seem to me that for byte literals of both `bytes` and `bytes32` types we may actually want to use the syntax of `b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xac\x58\xcd"` with the `b` prepended, as it is more Pythonic?
Starting on this. I think support for string & non-string notation literals should be allowed. As both are equally readable. However I will force the string to be 32 byte size in all scenarios, because of left/right padding it can be confusing if we don't enforce 32 byte size.

Resolving patch:
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
@@ -274,6 +274,9 @@ def base_type_conversion(orig, frm, to, pos):
     orig = unwrap_location(orig)
     if getattr(frm, 'is_literal', False) and frm.typ in ('int128', 'uint256') and not SizeLimits.in_bounds(frm.typ, orig.value):
         raise InvalidLiteralException("Number out of range: " + str(orig.value), pos)
+    # # Valid bytes[32] to bytes32 assignment.
+    # if isinstance(to, BaseType) and to.typ = 'bytes32' and isinstance(frm, ByteArrayType) and frm.maxlen == 32:
+    #     return LLLnode(orig.value, orig.args, typ=to, add_gas_estimate=orig.add_gas_estimate)
     if not isinstance(frm, (BaseType, NullType)) or not isinstance(to, BaseType):
         raise TypeMismatchException("Base type conversion from or to non-base type: %r %r" % (frm, to), pos)
     elif is_base_type(frm, to.typ) and are_units_compatible(frm, to):
diff --git a/vyper/parser/stmt.py b/vyper/parser/stmt.py
--- a/vyper/parser/stmt.py
+++ b/vyper/parser/stmt.py
@@ -42,7 +42,9 @@
 from vyper.utils import (
     SizeLimits,
     sha3,
-    fourbytes_to_int
+    fourbytes_to_int,
+    string_to_bytes,
+    bytes_to_int
 )
 from vyper.parser.expr import (
     Expr
@@ -95,6 +97,17 @@ def _check_valid_assign(self, sub):
         if isinstance(self.stmt.annotation, ast.Call):  # unit style: num(wei)
             if self.stmt.annotation.func.id != sub.typ.typ and not sub.typ.is_literal:
                 raise TypeMismatchException('Invalid type, expected: %s' % self.stmt.annotation.func.id, self.stmt)
+        elif isinstance(self.stmt.annotation, ast.Name) and self.stmt.annotation.id == 'bytes32':
+            if isinstance(sub.typ, ByteArrayType):
+                if sub.typ.maxlen != 32:
+                    raise TypeMismatchException('Invalid type, expected: bytes32. String is incorrect length.', self.stmt)
+                return
+            elif isinstance(sub.typ, BaseType):
+                if sub.typ.typ != 'bytes32':
+                    raise TypeMismatchException('Invalid type, expected: bytes32', self.stmt)
+                return
+            else:
+                raise TypeMismatchException('Invalid type, expected: bytes32', self.stmt)
         elif isinstance(self.stmt.annotation, ast.Dict):
             if not isinstance(sub.typ, StructType):
                 raise TypeMismatchException('Invalid type, expected a struct')
@@ -147,6 +160,10 @@ def ann_assign(self):
         o = LLLnode.from_list('pass', typ=None, pos=pos)
         if self.stmt.value is not None:
             sub = Expr(self.stmt.value, self.context).lll_node
+            # If bytes[32] to bytes32 assignment rewrite sub as bytes32.
+            if isinstance(sub.typ, ByteArrayType) and sub.typ.maxlen == 32 and isinstance(typ, BaseType) and typ.typ == 'bytes32':
+                bytez, bytez_length = string_to_bytes(self.stmt.value.s)
+                sub = LLLnode(bytes_to_int(bytez), typ=BaseType('bytes32'), pos=getpos(self.stmt))
             self._check_valid_assign(sub)
             self._check_same_variable_assign(sub)
             variable_loc = LLLnode.from_list(pos, typ=typ, location='memory', pos=getpos(self.stmt))


Test patch:
diff --git a/tests/parser/types/test_bytes.py b/tests/parser/types/test_bytes.py
--- a/tests/parser/types/test_bytes.py
+++ b/tests/parser/types/test_bytes.py
@@ -4,7 +4,7 @@
 )
 
 
-def test_address_bytes_converstion(get_contract_with_gas_estimation):
+def test_address_bytes_conversion(get_contract_with_gas_estimation):
     # Shared values for test addresses and bytes
     test_address = "0xF5D4020dCA6a62bB1efFcC9212AAF3c9819E30D7"
     test_bytes = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF5\xD4\x02\x0d\xCA\x6a\x62\xbB\x1e\xfF\xcC\x92\x12\xAA\xF3\xc9\x81\x9E\x30\xD7"
@@ -275,3 +275,18 @@ def get(a: bytes[100]) -> bool:
         lambda: get_contract_with_gas_estimation(code),
         ParserException
     )
+
+
+def test_bytes32_literals(get_contract):
+    code = """
+@public
+def test() -> bool:
+    l: bytes32 = '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x80\\xac\\x58\\xcd'
+    j: bytes32 = 0x0000000000000000000000000000000000000000000000000000000080ac58cd
+    return l == j
+
+    """
+
+    c = get_contract(code)
+
+    assert c.test() is True