Minimal solc compilation to web3 contracts.

pull/224/merge^2
Kieran Prasch 2018-04-12 13:05:16 -07:00 committed by tuxxy
parent 15be632621
commit 157bf90d96
4 changed files with 89 additions and 40 deletions

View File

@ -1,7 +1,5 @@
import random
from abc import ABC
from typing import List
from nkms.config.configs import EthereumConfig
@ -28,7 +26,7 @@ class TheBlockchain(ABC):
class IsAlreadyRunning(RuntimeError):
pass
def __init__(self, eth_config: EthereumConfig):
def __init__(self, config: EthereumConfig):
"""
Configures a populus project and connects to blockchain.network.
Transaction timeouts specified measured in seconds.
@ -43,8 +41,7 @@ class TheBlockchain(ABC):
raise TheBlockchain.IsAlreadyRunning(message)
TheBlockchain.__instance = self
self._eth_config = eth_config
self._chain = eth_config.provider # TODO
self.config = config
@classmethod
def get(cls):
@ -53,12 +50,6 @@ class TheBlockchain(ABC):
raise Exception('{} has not been created.'.format(class_name))
return cls.__instance
def disconnect(self):
self._chain.__exit__(None, None, None)
def __del__(self):
self.disconnect()
def __repr__(self):
class_name = self.__class__.__name__
r = "{}(network={})"
@ -66,17 +57,17 @@ class TheBlockchain(ABC):
def get_contract(self, name):
"""
Gets an existing contract from the network,
Gets an existing contract from the registrar,
or raises populus.contracts.exceptions.UnknownContract
if there is no contract data available for the name/identifier.
"""
return self._chain.provider.get_contract(name)
return self.config.provider.get_contract(name)
def wait_for_receipt(self, txhash, timeout=None) -> None:
if timeout is None:
timeout = self._default_timeout
result = self._chain.wait.for_receipt(txhash, timeout=timeout)
result = self.config.wait.for_receipt(txhash, timeout=timeout)
return result
@ -88,7 +79,7 @@ class TesterBlockchain(TheBlockchain):
def wait_time(self, wait_hours, step=50):
"""Wait the specified number of wait_hours by comparing block timestamps."""
end_timestamp = self._chain.web3.eth.getBlock(
end_timestamp = self.config.web3.eth.getBlock(
self._chain.web3.eth.blockNumber).timestamp + wait_hours * 60 * 60
while self._chain.web3.eth.getBlock(self._chain.web3.eth.blockNumber).timestamp < end_timestamp:
self._chain.wait.for_block(self._chain.web3.eth.blockNumber + step)

View File

@ -156,7 +156,7 @@ class NuCypherKMSTokenDeployer(ContractDeployer, NuCypherTokenConfig):
def __init__(self, blockchain):
super().__init__(blockchain=blockchain)
self._creator = self.blockchain._eth_config.provider.get_accounts()[0] # TODO: make swappable
self._creator = self.blockchain.config.provider.get_accounts()[0] # TODO: make swappable
def deploy(self) -> str:
"""

View File

@ -0,0 +1,40 @@
import os
from os.path import join, dirname, abspath
from solc import compile_files
from solc import install_solc
from nkms.blockchain import eth
class SolidityConfig:
version = 'v0.4.20'
contract_names = ('Issuer',
'NuCypherKMSToken',
'MinersEscrow',
'PolicyManager',
'UserEscrow')
__sol_binary_path = os.path.join(os.environ['VIRTUAL_ENV'], 'bin', 'solc')
_source_dir = join(dirname(abspath(eth.__file__)), 'sol', 'source', 'contracts')
def __init__(self):
os.environ['SOLC_BINARY'] = self.__sol_binary_path
def install_compiler(self):
# https://github.com/ethereum/py-solc#installing-the-solc-binary
return install_solc(self.version)
def compile_interfaces(config: SolidityConfig=SolidityConfig()) -> dict:
contract_paths = [os.path.join(config._source_dir, contract+'.sol') for contract in config.contract_names]
compiled_sol = compile_files(contract_paths)
interfaces = dict()
for contract_name, contract_path in zip(config.contract_names, contract_paths):
contract_interface = compiled_sol['{}:{}'.format(contract_path, contract_name)]
interfaces[contract_name] = contract_interface
return interfaces

View File

@ -1,12 +1,16 @@
import json
import os
from abc import ABC
from pathlib import Path
from typing import List, Union
import maya
from os.path import join, dirname, abspath
from eth_tester import EthereumTester, PyEVMBackend
from web3 import Web3, EthereumTesterProvider
from web3.contract import ConciseContract
from web3.contract import Contract
from web3.providers.tester import EthereumTesterProvider
from nkms.blockchain import eth
from nkms.blockchain.eth.sol.compile import compile_interfaces, SolidityConfig
_DEFAULT_CONFIGURATION_DIR = os.path.join(str(Path.home()), '.nucypher')
@ -15,34 +19,48 @@ class KMSConfigurationError(RuntimeError):
pass
class BlockchainConfig(ABC):
pass
class KMSProvider:
def __init__(self, provider=None, registrar=None):
class EthereumConfig(BlockchainConfig):
__solididty_source_dir = join(dirname(abspath(eth.__file__)), 'sol_source')
__default_registrar_path = join(_DEFAULT_CONFIGURATION_DIR, 'registrar.json')
def __init__(self, provider, registrar_path=None):
if provider is None:
# https: // github.com / ethereum / eth - tester # available-backends
eth_tester = EthereumTester(backend=PyEVMBackend()) # TODO: Discuss backend choice
provider = EthereumTesterProvider(ethereum_tester=eth_tester, api_endpoints=None)
self.provider = provider
self.w3 = Web3(self.provider)
if registrar_path is None:
registrar_path = self.__default_registrar_path
self._registrar_path = registrar_path
self.__registrar = None
# Populus project config
# self._populus_project = populus.Project(self._project_dir)
# self.project.config['chains.mainnetrpc.contracts.backends.JSONFile.settings.file_path'] = self._registrar_path
def __make_web3_contracts(self, contract_factory: Union[ConciseContract, Contract]=ConciseContract, address=None) -> List[Contract]:
"""Instantiate web3 Contracts from raw contract interface data with the supplied web3 provider"""
sol_config = SolidityConfig()
interfaces = compile_interfaces(config=sol_config)
# @property
# def project(self):
# return self._populus_project
if contract_factory is ConciseContract and address is None:
raise Exception('Address must be provided when making concise contracts.')
elif contract_factory is Contract and address is not None:
raise Exception('Address must not be provided when making deployable, non-concise contracts')
web3_contracts = list()
for contract, interface in interfaces.items():
contract = self.w3.eth.contract(abi=interface['abi'],
bytecode=interface['bin'],
ContractFactoryClass=contract_factory)
web3_contracts.append(contract)
return web3_contracts
def get_contract(self):
pass
def deploy_contract(self):
pass
class StakeConfig:
__minimum_stake_amount = 0 # TODO!!!
__minimum_stake_duration = 0
# __minimum_stake_amount = 0 # TODO
# __minimum_stake_duration = 0
def __init__(self, amount: int, periods: int, start_datetime):
@ -54,9 +72,9 @@ class StakeConfig:
@classmethod
def validate_stake(cls, amount: int, periods: int, start_datetime) -> bool:
rules = (
(amount > cls.__minimum_stake_amount, 'Staking aount must be at least {min_amount}'),
# (amount > cls.__minimum_stake_amount, 'Staking aount must be at least {min_amount}'), # TODO
(start_datetime < maya.now(), 'Start date/time must not be in the past.'),
(periods > cls.__minimum_stake_duration, 'Staking duration must be at least {}'.format(cls.__minimum_stake_duration))
# (periods > cls.__minimum_stake_duration, 'Staking duration must be at least {}'.format(cls.__minimum_stake_duration))
)
for rule, failure_message in rules: