Skip to content

Commit

Permalink
support deploying Solidity contracts, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
caktux committed Feb 18, 2015
1 parent 7267937 commit 6517506
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ logging.conf
*.log
.coverage
*.se
*.sol
*.binary
*.yaml
MANIFEST
upload
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ You will need a package definition file in YAML format to get started (see examp
deploy:
extra:
contract: short_namecoin.se
-
# Deploy Solidity contract
deploy:
Config:
contract: config.sol
solidity:
- Config
- mortal
- owned
```

## Usage
Expand Down
44 changes: 38 additions & 6 deletions pyepm/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
# @Author: caktux
# @Date: 2014-12-21 12:44:20
# @Last Modified by: caktux
# @Last Modified time: 2015-02-17 18:41:40
# @Last Modified time: 2015-02-17 23:29:14

import logging

import os
import api
import json
import yaml
import subprocess

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -45,6 +46,7 @@ def deploy(self, wait=False):
if key == 'deploy':
for name in definition[key]:
# Reset default values at each definition
contract_names = []
from_ = default_from
gas = default_gas
gas_price = default_gas_price
Expand All @@ -53,6 +55,8 @@ def deploy(self, wait=False):
for option in definition[key][name]:
if option == 'contract':
contract = definition[key][name][option]
if option == 'solidity':
contract_names = definition[key][name][option]
if option == 'from':
from_ = definition[key][name][option]
if option == 'gas':
Expand All @@ -64,7 +68,7 @@ def deploy(self, wait=False):
if option == 'wait':
wait = definition[key][name][option]
logger.info(" Deploying %s..." % os.path.join(path, contract))
contract_address = self.create("%s" % os.path.join(path, contract), from_, gas, gas_price, value, wait)
contract_address = self.create("%s" % os.path.join(path, contract), from_, gas, gas_price, value, wait, contract_names=contract_names)
definitions = self.replace(name, definitions, contract_address, True)
logger.debug(definitions)

Expand Down Expand Up @@ -114,14 +118,42 @@ def deploy(self, wait=False):
elif key == 'call':
self.call(to, from_, fun_name, sig, data, gas, gas_price, value, wait)

def create(self, contract, from_, gas, gas_price, value, wait):
def compile_solidity(self, contract, contract_names=[]):
subprocess.call(["solc", "--input-file", contract, "--binary", "file"])
contracts = []
if not isinstance(contract_names, list):
raise Exception("Contract names must be list")
if not contract_names:
contract_names = [contract[:-4]]
for contract_name in contract_names:
filename = "%s.binary" % contract_name
evm = "0x" + open(filename).read()
contracts.append((contract_name, evm))
return contracts

def create(self, contract, from_, gas, gas_price, value, wait, contract_names=None):
instance = api.Api(self.config)
contract = compile(open(contract).read()).encode('hex')
contract_address = instance.create(contract, from_=from_, gas=gas, gas_price=gas_price, endowment=value)
logger.info(" Contract is available at %s" % contract_address)
contract_addresses = []
if contract[-3:] == 'sol' or contract_names:
contracts = self.compile_solidity(contract, contract_names)
if contract_names:
for contract_name, contract in contracts:
logger.info("%s: %s" % (contract_name, contract))
contract_address = instance.create(contract, from_=from_, gas=gas, gas_price=gas_price, endowment=value)
contract_addresses.append(contract_address)
logger.info(" Contract '%s' is available at %s" % (contract_name, contract_address))
else:
contract_address = instance.create(contract, from_=from_, gas=gas, gas_price=gas_price, endowment=value)
logger.info(" Contract is available at %s" % contract_address)
else:
contract = compile(open(contract).read()).encode('hex')
contract_address = instance.create(contract, from_=from_, gas=gas, gas_price=gas_price, endowment=value)
logger.info(" Contract is available at %s" % contract_address)
if wait:
instance.wait_for_next_block(verbose=(True if self.config.get('misc', 'verbosity') > 1 else False))

if contract_addresses:
return contract_addresses
return contract_address

def transact(self, to, from_, fun_name, sig, data, gas, gas_price, value, wait):
Expand Down
9 changes: 9 additions & 0 deletions test/fixtures/example.yaml.fixture
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,12 @@
deploy:
extra:
contract: short_namecoin.se
-
# Deploy Solidity contract
deploy:
Config:
contract: config.sol
solidity:
- Config
- mortal
- owned
29 changes: 28 additions & 1 deletion test/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest
import requests

from pyepm import config
from pyepm import deploy, config
config = config.get_default_config()

from pyepm import api # NOQA
Expand Down Expand Up @@ -111,6 +111,33 @@ def test_is_contract_at_contract_doesnt_exists(mocker):
assert not mock_rpc(mocker, 'is_contract_at', [address], json_result=code,
rpc_method='eth_codeAt', rpc_params=[address])

def test_create_solidity(mocker):
contract = 'test/fixtures/config.sol'
deployment = deploy.Deploy(contract, config)
contracts = deployment.compile_solidity(contract, ['Config', 'mortal', 'owned'])

address = '0x6489ecbe173ac43dadb9f4f098c3e663e8438dd7'
for contract_name, code in contracts:
rpc_params = [{'gas': '10000',
'code': code,
'from': 'cd2a3d9f938e13cd947ec05abc7fe734df8dd826',
'value': '0',
'gasPrice': '10000000000000'}]
assert mock_rpc(mocker, 'create', [code], json_result=address,
rpc_method='eth_transact', rpc_params=rpc_params) == address

def test_is_solidity_contract_at_contract_exists(mocker):
address = '0x6489ecbe173ac43dadb9f4f098c3e663e8438dd7'
code = '0xdeadbeef'
assert mock_rpc(mocker, 'is_contract_at', [address], json_result=code,
rpc_method='eth_codeAt', rpc_params=[address])

def test_is_solidity_contract_at_contract_doesnt_exists(mocker):
address = '0x6489ecbe173ac43dadb9f4f098c3e663e8438dd7'
code = '0x0000000000000000000000000000000000000000000000000000000000000000'
assert not mock_rpc(mocker, 'is_contract_at', [address], json_result=code,
rpc_method='eth_codeAt', rpc_params=[address])

def test_is_listening(mocker):
assert mock_rpc(mocker, 'is_listening', [], json_result=True,
rpc_method='eth_listening', rpc_params=None)
Expand Down
4 changes: 3 additions & 1 deletion test/test_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ def test_load_yaml():
'funid': 1,
'to': '$Subcurrency',
'value': 0}}},
{'deploy': {'extra': {'contract': 'short_namecoin.se'}}}]
{'deploy': {'extra': {'contract': 'short_namecoin.se'}}},
{'deploy': {'Config': {'contract': 'config.sol',
'solidity': ['Config', 'mortal', 'owned']}}}]

0 comments on commit 6517506

Please sign in to comment.