Repository: vyperlang/vyper

Issue:
VIP: Add support for EXTCODEHASH
## Simple Summary
Add support for the `EXTCODEHASH` opcode, which was added in Constantinople via [EIP-1052](https://eips.ethereum.org/EIPS/eip-1052) which allows a user to obtain the `keccak256` hash of a contract's deployed bytecode.

## Motivation
This feature is helpful for users who want to check that a particular configuration of a contract's bytecode is a given value, such as the use of proxy/registry contracts, and other uses.

## Specification
Add the attribute `address.codehash` which returns the hash of the given account's address per EIP-1052 by calling that opcode. Note the updated gas costs in #1653.

## Backwards Compatibility
No backwards incompatibilities.

## Dependencies
No dependencies

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/)

---

## Edits:
#1818 - Modified syntax from `get_extcodehash(address)` to `address.codehash`



Discussion:
This will be easy to implement after #1783 - and give us something to list in the documentation under changes when targeting `constantinople` :)

Resolving patch:
diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py
--- a/vyper/functions/functions.py
+++ b/vyper/functions/functions.py
@@ -3,6 +3,7 @@
 from vyper import ast
 from vyper.exceptions import (
     ConstancyViolationException,
+    EvmVersionException,
     InvalidLiteralException,
     ParserException,
     StructureException,
@@ -423,6 +424,20 @@ def sha256(expr, args, kwargs, context):
         raise Exception(f"Unsupported location: {sub.location}")  # pragma: no test
 
 
+@signature('address')
+def get_extcodehash(expr, args, kwargs, context):
+    if not version_check(begin="constantinople"):
+        raise EvmVersionException(
+            "get_extcodehash is unavailable prior to constantinople ruleset",
+            expr
+        )
+    return LLLnode.from_list(
+        ['extcodehash', args[0]],
+        typ=BaseType('bytes32'),
+        pos=getpos(expr)
+    )
+
+
 @signature('str_literal', 'name_literal')
 def method_id(expr, args, kwargs, context):
     if b' ' in args[0]:
@@ -1296,6 +1311,7 @@ def _clear():
     'concat': concat,
     'sha3': _sha3,
     'sha256': sha256,
+    'get_extcodehash': get_extcodehash,
     'method_id': method_id,
     'keccak256': _keccak256,
     'ecrecover': ecrecover,


Test patch:
diff --git a/tests/parser/functions/test_get_hash.py b/tests/parser/functions/test_get_hash.py
new file mode 100644
--- /dev/null
+++ b/tests/parser/functions/test_get_hash.py
@@ -0,0 +1,63 @@
+
+import pytest
+
+from vyper.compiler import (
+    compile_code,
+)
+from vyper.exceptions import (
+    EvmVersionException,
+)
+from vyper.opcodes import (
+    EVM_VERSIONS,
+)
+from vyper.utils import (
+    keccak256,
+)
+
+
+@pytest.mark.parametrize('evm_version', list(EVM_VERSIONS))
+def test_get_extcodehash(get_contract, evm_version):
+    code = """
+a: address
+
+@public
+def __init__():
+    self.a = self
+
+@public
+def foo(x: address) -> bytes32:
+    return get_extcodehash(x)
+
+@public
+def foo2(x: address) -> bytes32:
+    b: address = x
+    return get_extcodehash(b)
+
+@public
+def foo3() -> bytes32:
+    return get_extcodehash(self)
+
+@public
+def foo4() -> bytes32:
+    return get_extcodehash(self.a)
+    """
+
+    if evm_version == "byzantium":
+        with pytest.raises(EvmVersionException):
+            compile_code(code, evm_version=evm_version)
+        return
+
+    compiled = compile_code(code, ['bytecode_runtime'], evm_version=evm_version)
+    bytecode = bytes.fromhex(compiled['bytecode_runtime'][2:])
+    hash_ = keccak256(bytecode)
+
+    c = get_contract(code, evm_version=evm_version)
+
+    assert c.foo(c.address) == hash_
+    assert not int(c.foo("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF").hex(), 16)
+
+    assert c.foo2(c.address) == hash_
+    assert not int(c.foo2("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF").hex(), 16)
+
+    assert c.foo3() == hash_
+    assert c.foo4() == hash_