Skip to content

Commit

Permalink
Merge branch 'master' of github.com:pipermerriam/populus
Browse files Browse the repository at this point in the history
  • Loading branch information
pipermerriam committed Oct 9, 2015
2 parents b2b31ba + b51552f commit e99066d
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 5 deletions.
3 changes: 3 additions & 0 deletions populus/contracts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
from populus.contracts.utils import ( # NOQA
get_max_gas,
deploy_contract,
get_linker_dependencies,
link_contract_dependency,
strip_0x_prefix,
)
from populus.contracts.functions import ( # NOQA
Function,
Expand Down
8 changes: 6 additions & 2 deletions populus/contracts/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,15 @@ class Config(object):
"""
Contract (class) level contract data.
"""
def __init__(self, code, source, abi, functions, events, constructor):
def __init__(self, code, source, abi, functions, events, constructor,
contract_name=None):
self.code = code
self.source = source
self.abi = abi
self._functions = functions
self._events = events
self.constructor = constructor
self.name = contract_name


def Contract(contract_meta, contract_name=None):
Expand Down Expand Up @@ -138,7 +140,9 @@ def Contract(contract_meta, contract_name=None):
)

_dict['__doc__'] = docstring
_dict['_config'] = Config(code, source, _abi, functions, events, constructor)
_dict['_config'] = Config(
code, source, _abi, functions, events, constructor, contract_name,
)

return type(str(contract_name), (ContractBase,), _dict)

Expand Down
33 changes: 32 additions & 1 deletion populus/contracts/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import binascii
import re

from ethereum import utils as ethereum_utils
from ethereum import abi
import binascii


def decode_single(typ, data):
Expand Down Expand Up @@ -77,3 +79,32 @@ def deploy_contract(rpc_client, contract_class, constructor_args=None, **kwargs)
kwargs['data'] = contract_class.get_deploy_data(*constructor_args)
txn_hash = rpc_client.send_transaction(**kwargs)
return txn_hash


DEPENDENCY_RE = re.compile((
r''
'__' # Prefixed by double underscore
'(?P<name>[A-Za-z0-9]+)' # capture the name of the dependency
'_{1,37}' # and then enough underscores to finish out the 40 chars.
))


def get_linker_dependencies(contracts):
dependencies = {
contract_name: set(DEPENDENCY_RE.findall(contract_meta._config.code))
for contract_name, contract_meta
in contracts
if '__' in contract_meta._config.code
}
return dependencies


def link_contract_dependency(contract, deployed_contract):
location_re = re.compile(
deployed_contract._config.name.ljust(38, "_").rjust(40, "_"),
)
contract._config.code = location_re.sub(
strip_0x_prefix(deployed_contract._meta.address),
contract._config.code,
)
return contract
15 changes: 14 additions & 1 deletion populus/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,13 @@ def deployed_contracts(request, populus_config, deploy_client, contracts):
from populus.contracts import (
deploy_contract,
get_max_gas,
get_linker_dependencies,
link_contract_dependency,
)
from populus.utils import (
wait_for_block,
get_contract_address_from_txn,
merge_dependencies,
)

_deployed_contracts = {}
Expand All @@ -182,13 +185,18 @@ def deployed_contracts(request, populus_config, deploy_client, contracts):
deploy_contracts = set(populus_config.get_value(
request, 'deploy_contracts',
))
deploy_dependencies = populus_config.get_value(
declared_dependencies = populus_config.get_value(
request, 'deploy_dependencies',
)
deploy_constructor_args = populus_config.get_value(
request, 'deploy_constructor_args',
)

linker_dependencies = get_linker_dependencies(contracts)
deploy_dependencies = merge_dependencies(
declared_dependencies, linker_dependencies,
)

if deploy_dependencies:
dependencies = copy.copy(deploy_dependencies)
for contract_name, _ in contracts:
Expand All @@ -211,6 +219,11 @@ def deployed_contracts(request, populus_config, deploy_client, contracts):
'deploy_gas_limit',
) or get_max_gas(deploy_client))

if contract_name in linker_dependencies:
for dependency_name in linker_dependencies[contract_name]:
deployed_contract = _deployed_contracts[dependency_name]
link_contract_dependency(contract_class, deployed_contract)

txn_hash = deploy_contract(
deploy_client,
contract_class,
Expand Down
18 changes: 18 additions & 0 deletions populus/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import socket
import time
import signal
import operator
import functools
import itertools


CONTRACTS_DIR = "./contracts/"
Expand Down Expand Up @@ -150,3 +153,18 @@ def get_contract_address_from_txn(rpc_client, txn_hash, max_wait=0):
txn_receipt = wait_for_transaction(rpc_client, txn_hash, max_wait)

return txn_receipt['contractAddress']


def merge_dependencies(*dependencies):
"""
Takes dictionaries of key => set(...) and merges them all into a single
dictionary where each key is the union of all of the sets for that key
across all dictionaries.
"""
return {
k: set(functools.reduce(
operator.or_,
(d.get(k, set()) for d in dependencies)
))
for k in itertools.chain.from_iterable((d.keys() for d in dependencies))
}
38 changes: 38 additions & 0 deletions tests/compile/test_merge_dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from populus.utils import (
merge_dependencies,
)


def test_empty_dependencies():
actual = merge_dependencies()
assert actual == dict()


def test_single_dependencies():
deps = {
'Test-1': set(('Dep1', 'Dep2')),
'Test-2': set(('Dep1',)),
}
actual = merge_dependencies(deps)
assert actual == deps


def test_multiple_dependencies():
deps_a = {
'Test-1': set(('Dep1', 'Dep2')),
'Test-2': set(('Dep1',)),
}
deps_b = {
'Test-2': set(('Dep3',)),
'Test-3': set(('Dep2', 'Dep3')),
}
deps_c = {
'Test-1': set(('Dep3',)),
}
actual = merge_dependencies(deps_a, deps_b, deps_c)
expected = {
'Test-1': set(('Dep1', 'Dep2', 'Dep3')),
'Test-2': set(('Dep1', 'Dep3')),
'Test-3': set(('Dep2', 'Dep3')),
}
assert actual == expected
121 changes: 121 additions & 0 deletions tests/contracts/projects/test-03/build/contracts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{
"AccountingLib": {
"code": "0x60606040526101ff806100126000396000f365010523865e395060606040526000357c0100000000000000000000000000000000000000000000000000000000900480634b260a311461005a578063538b9e491461008f578063dae1104a146100a757610055565b610007565b610079600480803590602001909190803590602001909190505061010a565b6040518082815260200191505060405180910390f35b6100a560048080359060200190919050506100c8565b005b6100c6600480803590602001909190803590602001909190505061014b565b005b348160000160005060003373ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828282505401925050819055505b50565b60008260000160005060008373ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050610145565b92915050565b8160000160005060003373ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060005054811115610189576101fb565b808260000160005060003373ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828282505403925050819055503373ffffffffffffffffffffffffffffffffffffffff16600082604051809050600060405180830381858888f19350505050505b505056",
"info": {
"abiDefinition": [
{
"constant": true,
"inputs": [
{
"name": "self",
"type": "AccountingLib.Ledger storage"
},
{
"name": "addr",
"type": "address"
}
],
"name": "balance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "self",
"type": "AccountingLib.Ledger storage"
}
],
"name": "deposit",
"outputs": [],
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "self",
"type": "AccountingLib.Ledger storage"
},
{
"name": "value",
"type": "uint256"
}
],
"name": "withdraw",
"outputs": [],
"type": "function"
}
],
"compilerVersion": "0.1.5-23865e39",
"developerDoc": {
"methods": {}
},
"language": "Solidity",
"languageVersion": "0",
"source": "library AccountingLib {\n struct Ledger {\n mapping (address => uint) balances;\n }\n\n function deposit(Ledger storage self) public {\n // check we haven't already registered\n self.balances[msg.sender] += msg.value;\n }\n\n function balance(Ledger storage self, address addr) constant returns (uint) {\n return self.balances[addr];\n }\n\n function withdraw(Ledger storage self, uint value) public {\n if (value > self.balances[msg.sender]) {\n // insufficient balance.\n return;\n }\n self.balances[msg.sender] -= value;\n msg.sender.send(value);\n }\n}\n\n\ncontract PiggyBank {\n AccountingLib.Ledger accounts;\n\n function deposit() public {\n AccountingLib.deposit(accounts);\n }\n\n function checkBalance(address acct) constant returns (uint) {\n return AccountingLib.balance(accounts, acct);\n }\n\n function withdraw(uint value) {\n AccountingLib.withdraw(accounts, value);\n }\n}\n",
"userDoc": {
"methods": {}
}
}
},
"PiggyBank": {
"code": "0x606060405261021f806100126000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480632e1a7d4d1461004f5780635f51522614610067578063d0e30db0146100935761004d565b005b61006560048080359060200190919050506101a9565b005b61007d600480803590602001909190505061010f565b6040518082815260200191505060405180910390f35b6100a060048050506100a2565b005b73__AccountingLib_________________________63538b9e496000600050604051827c01000000000000000000000000000000000000000000000000000000000281526004018082815260200191505060006040518083038160008760325a03f215610002575050505b565b600073__AccountingLib_________________________634b260a31600060005084604051837c0100000000000000000000000000000000000000000000000000000000028152600401808381526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019250505060206040518083038160008760325a03f215610002575050506040515190506101a4565b919050565b73__AccountingLib_________________________63dae1104a600060005083604051837c0100000000000000000000000000000000000000000000000000000000028152600401808381526020018281526020019250505060006040518083038160008760325a03f215610002575050505b5056",
"info": {
"abiDefinition": [
{
"constant": false,
"inputs": [
{
"name": "value",
"type": "uint256"
}
],
"name": "withdraw",
"outputs": [],
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "acct",
"type": "address"
}
],
"name": "checkBalance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "deposit",
"outputs": [],
"type": "function"
}
],
"compilerVersion": "0.1.5-23865e39",
"developerDoc": {
"methods": {}
},
"language": "Solidity",
"languageVersion": "0",
"source": "library AccountingLib {\n struct Ledger {\n mapping (address => uint) balances;\n }\n\n function deposit(Ledger storage self) public {\n // check we haven't already registered\n self.balances[msg.sender] += msg.value;\n }\n\n function balance(Ledger storage self, address addr) constant returns (uint) {\n return self.balances[addr];\n }\n\n function withdraw(Ledger storage self, uint value) public {\n if (value > self.balances[msg.sender]) {\n // insufficient balance.\n return;\n }\n self.balances[msg.sender] -= value;\n msg.sender.send(value);\n }\n}\n\n\ncontract PiggyBank {\n AccountingLib.Ledger accounts;\n\n function deposit() public {\n AccountingLib.deposit(accounts);\n }\n\n function checkBalance(address acct) constant returns (uint) {\n return AccountingLib.balance(accounts, acct);\n }\n\n function withdraw(uint value) {\n AccountingLib.withdraw(accounts, value);\n }\n}\n",
"userDoc": {
"methods": {}
}
}
}
}
40 changes: 40 additions & 0 deletions tests/contracts/projects/test-03/contracts/Ledger.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
library AccountingLib {
struct Ledger {
mapping (address => uint) balances;
}

function deposit(Ledger storage self) public {
// check we haven't already registered
self.balances[msg.sender] += msg.value;
}

function balance(Ledger storage self, address addr) constant returns (uint) {
return self.balances[addr];
}

function withdraw(Ledger storage self, uint value) public {
if (value > self.balances[msg.sender]) {
// insufficient balance.
return;
}
self.balances[msg.sender] -= value;
msg.sender.send(value);
}
}


contract PiggyBank {
AccountingLib.Ledger accounts;

function deposit() public {
AccountingLib.deposit(accounts);
}

function checkBalance(address acct) constant returns (uint) {
return AccountingLib.balance(accounts, acct);
}

function withdraw(uint value) {
AccountingLib.withdraw(accounts, value);
}
}
24 changes: 24 additions & 0 deletions tests/contracts/test_finding_linker_dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import os

from populus.utils import load_contracts
from populus.contracts import (
package_contracts,
)
from populus.contracts.utils import (
get_linker_dependencies,
)


PROJECTS_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'projects')
project_dir = os.path.join(PROJECTS_DIR, 'test-03')


def test_extracting_linker_dependencies():
contracts = package_contracts(load_contracts(project_dir))

linker_deps = get_linker_dependencies(contracts)
actual = {
"PiggyBank": set(("AccountingLib",)),
}

assert linker_deps == actual
Loading

0 comments on commit e99066d

Please sign in to comment.