From d3de282409d953153868ca72198540b54666696b Mon Sep 17 00:00:00 2001
From: Ryan Casey <ryepdx@gmail.com>
Date: Sun, 14 Jun 2015 16:11:44 -0700
Subject: [PATCH 1/3] Add support for Solidity constructors.

---
 ethereum/abi.py                 | 27 +++++++++++++---------
 ethereum/tester.py              | 22 ++++++++++++++----
 ethereum/tests/test_solidity.py | 41 ++++++++++++++++++++++++++++++---
 3 files changed, 71 insertions(+), 19 deletions(-)

diff --git a/ethereum/abi.py b/ethereum/abi.py
index 2662a7071..94d8499f9 100644
--- a/ethereum/abi.py
+++ b/ethereum/abi.py
@@ -13,7 +13,7 @@ def json_decode(x):
 
 class ContractTranslator():
 
-    def __init__(self, full_signature):
+    def __init__(self, full_signature, contract_name='__contract__'):
         self.function_data = {}
         self.event_data = {}
         v = vars(self)
@@ -22,7 +22,7 @@ def __init__(self, full_signature):
         for sig_item in full_signature:
             encode_types = [f['type'] for f in sig_item['inputs']]
             signature = [(f['type'], f['name']) for f in sig_item['inputs']]
-            name = sig_item['name']
+            name = sig_item.get('name', contract_name)
             if '(' in name:
                 name = name[:name.find('(')]
             if name in v:
@@ -34,19 +34,23 @@ def __init__(self, full_signature):
                                  " name. Use %s to call %s with types %r"
                                  % (name, sig_item['name'], encode_types))
             sig = name + '(' + ','.join(encode_types) + ')'
-            if sig_item['type'] == 'function':
-                prefix = big_endian_to_int(utils.sha3(sig)[:4])
-                decode_types = [f['type'] for f in sig_item['outputs']]
-                is_unknown_type = len(sig_item['outputs']) and \
+            if sig_item['type'] in ('function', 'constructor'):
+                decode_types = [f['type'] for f in sig_item.get('outputs', [])]
+                is_unknown_type = len(decode_types) > 0 and \
                     sig_item['outputs'][0]['name'] == 'unknown_out'
-                self.function_data[name] = {
-                    "prefix": prefix,
+                func = {
                     "encode_types": encode_types,
                     "decode_types": decode_types,
                     "is_unknown_type": is_unknown_type,
                     "is_constant": sig_item.get('constant', False),
                     "signature": signature
                 }
+
+                if sig_item['type'] == 'function':
+                    func['prefix'] = big_endian_to_int(utils.sha3(sig)[:4])
+
+                self.function_data[name] = func
+
             elif sig_item['type'] == 'event':
                 prefix = big_endian_to_int(utils.sha3(sig))
                 indexed = [f['indexed'] for f in sig_item['inputs']]
@@ -60,9 +64,10 @@ def __init__(self, full_signature):
 
     def encode(self, name, args):
         fdata = self.function_data[name]
-        o = zpad(encode_int(fdata['prefix']), 4) + \
-            encode_abi(fdata['encode_types'], args)
-        return o
+        prefix = ''
+        if 'prefix' in fdata:
+            prefix = zpad(encode_int(fdata['prefix']), 4)
+        return prefix + encode_abi(fdata['encode_types'], args)
 
     def decode(self, name, data):
         # print 'out', data.encode('hex')
diff --git a/ethereum/tester.py b/ethereum/tester.py
index 0e9c3f07c..85bb495d3 100644
--- a/ethereum/tester.py
+++ b/ethereum/tester.py
@@ -7,7 +7,7 @@
 import ethereum.opcodes as opcodes
 import ethereum.abi as abi
 from ethereum.slogging import LogRecorder, configure_logging, set_level
-from ethereum.utils import to_string
+from ethereum.utils import to_string, is_string
 from ethereum._solidity import get_solidity
 import rlp
 from rlp.utils import decode_hex, encode_hex, ascii_chr
@@ -132,7 +132,8 @@ def __init__(self, num_accounts=len(keys)):
     def __del__(self):
         shutil.rmtree(self.temp_data_dir)
 
-    def contract(self, code, sender=k0, endowment=0, language='serpent', gas=None):
+    def contract(self, code, sender=k0, endowment=0, language='serpent',
+                 gas=None, constructor_args=[]):
         if language not in languages:
             languages[language] = __import__(language)
         language = languages[language]
@@ -141,8 +142,9 @@ def contract(self, code, sender=k0, endowment=0, language='serpent', gas=None):
         assert len(self.block.get_code(o)), "Contract code empty"
         return o
 
-    def abi_contract(self, code, sender=k0, endowment=0, language='serpent', contract_name='',
-                     gas=None, log_listener=None, listen=True):
+    def abi_contract(self, code, sender=k0, endowment=0, language='serpent',
+                     contract_name='', gas=None, log_listener=None, listen=True,
+                     constructor_args=[]):
         if contract_name:
             assert language == 'solidity'
             cn_args = dict(contract_name=contract_name)
@@ -151,10 +153,20 @@ def abi_contract(self, code, sender=k0, endowment=0, language='serpent', contrac
         if language not in languages:
             languages[language] = __import__(language)
         language = languages[language]
+        _abi = language.mk_full_signature(code, **cn_args)
+        if is_string(_abi):
+            _abi = abi.json_decode(_abi)
+
         evm = language.compile(code, **cn_args)
+
+        if len([i for i in _abi if i['type'] == 'constructor']) > 0 \
+            and len(constructor_args) > 0:
+            cname = contract_name or '__contract__'
+            translator = abi.ContractTranslator(_abi, contract_name=cname)
+            evm += translator.encode(cname, constructor_args)
+
         address = self.evm(evm, sender, endowment, gas)
         assert len(self.block.get_code(address)), "Contract code empty"
-        _abi = language.mk_full_signature(code, **cn_args)
         return ABIContract(self, _abi, address, listen=listen, log_listener=log_listener)
 
 
diff --git a/ethereum/tests/test_solidity.py b/ethereum/tests/test_solidity.py
index 00dd858b6..c85537622 100644
--- a/ethereum/tests/test_solidity.py
+++ b/ethereum/tests/test_solidity.py
@@ -1,4 +1,13 @@
+from rlp.utils import encode_hex
 from ethereum import tester
+
+def needs_solidity(function):
+    def decorated(*args, **kwargs):
+        if 'solidity' in tester.languages:
+            return function(*args, **kwargs)
+
+    return decorated
+
 serpent_contract = """
 extern solidity: [sub2:_:i]
 
@@ -23,10 +32,8 @@ def sub1():
 }
 """
 
-
+@needs_solidity
 def test_interop():
-    if 'solidity' not in tester.languages:
-        return
     s = tester.state()
     c1 = s.abi_contract(serpent_contract)
     c2 = s.abi_contract(solidity_contract, language='solidity')  # should be zoo
@@ -34,3 +41,31 @@ def test_interop():
     assert c2.sub2() == 7
     assert c1.main(c2.address) == 14
     assert c2.main(c1.address) == 10
+
+constructor_contract = """
+contract gondor {
+    address public ruler;
+
+    function gondor(address steward) {
+        if (steward == 0x0) {
+            ruler = msg.sender;
+        } else {
+            ruler = steward;
+        }
+    }
+}
+"""
+
+@needs_solidity
+def test_constructor():
+    s = tester.state()
+    c1 = s.abi_contract(
+        constructor_contract, language='solidity',
+        contract_name='gondor'
+    )
+    c2 = s.abi_contract(
+        constructor_contract, constructor_args=[tester.a1],
+        language='solidity', contract_name='gondor'
+    )
+    assert c1.ruler() != c2.ruler()
+    assert c2.ruler() == encode_hex(tester.a1)

From 17031f51aaa0ced5e5ddf149dfb6b0d182826e80 Mon Sep 17 00:00:00 2001
From: Ryan Casey <ryepdx@gmail.com>
Date: Sun, 14 Jun 2015 16:31:01 -0700
Subject: [PATCH 2/3] Enable passing constructor arguments to tester.contract.

---
 ethereum/tester.py              |  5 +++++
 ethereum/tests/test_solidity.py | 17 ++++++++++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/ethereum/tester.py b/ethereum/tester.py
index 85bb495d3..219657298 100644
--- a/ethereum/tester.py
+++ b/ethereum/tester.py
@@ -138,6 +138,11 @@ def contract(self, code, sender=k0, endowment=0, language='serpent',
             languages[language] = __import__(language)
         language = languages[language]
         evm = language.compile(code)
+        if len(constructor_args) > 0:
+            evm += abi.encode_abi(
+                [a['type'] for a in constructor_args],
+                [a['val'] for a  in constructor_args])
+
         o = self.evm(evm, sender, endowment)
         assert len(self.block.get_code(o)), "Contract code empty"
         return o
diff --git a/ethereum/tests/test_solidity.py b/ethereum/tests/test_solidity.py
index c85537622..7ab849032 100644
--- a/ethereum/tests/test_solidity.py
+++ b/ethereum/tests/test_solidity.py
@@ -57,7 +57,7 @@ def test_interop():
 """
 
 @needs_solidity
-def test_constructor():
+def test_abi_constructor():
     s = tester.state()
     c1 = s.abi_contract(
         constructor_contract, language='solidity',
@@ -69,3 +69,18 @@ def test_constructor():
     )
     assert c1.ruler() != c2.ruler()
     assert c2.ruler() == encode_hex(tester.a1)
+
+@needs_solidity
+def test_constructor():
+    s = tester.state()
+    a1 = s.contract(constructor_contract, language='solidity')
+    a2 = s.contract(
+        constructor_contract, constructor_args=[
+            {'type': 'address', 'val': tester.a1
+        }], language='solidity'
+    )
+    _abi = tester.languages['solidity'].mk_full_signature(constructor_contract)
+    c1 = tester.ABIContract(s, _abi, a1)
+    c2 = tester.ABIContract(s, _abi, a2)
+    assert c1.ruler() != c2.ruler()
+    assert c2.ruler() == encode_hex(tester.a1)

From 24581bbc698855acca16ea85c26212cb02d4cecd Mon Sep 17 00:00:00 2001
From: Ryan Casey <ryepdx@gmail.com>
Date: Sun, 14 Jun 2015 16:48:29 -0700
Subject: [PATCH 3/3] Moved a brace.

---
 ethereum/tests/test_solidity.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ethereum/tests/test_solidity.py b/ethereum/tests/test_solidity.py
index 7ab849032..bcef79440 100644
--- a/ethereum/tests/test_solidity.py
+++ b/ethereum/tests/test_solidity.py
@@ -76,8 +76,8 @@ def test_constructor():
     a1 = s.contract(constructor_contract, language='solidity')
     a2 = s.contract(
         constructor_contract, constructor_args=[
-            {'type': 'address', 'val': tester.a1
-        }], language='solidity'
+            {'type': 'address', 'val': tester.a1}
+        ], language='solidity'
     )
     _abi = tester.languages['solidity'].mk_full_signature(constructor_contract)
     c1 = tester.ABIContract(s, _abi, a1)