Merge pull request #1470 from vzotova/upgradability-test

Versioning of contracts and upgradability test
pull/1542/head
David Núñez 2019-12-26 09:58:03 +00:00 committed by GitHub
commit 11e7fc6c92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 937 additions and 436 deletions

View File

@ -122,3 +122,9 @@ All tokens will be unlocked after a specified time and the user can retrieve the
When the user wants to become a staker - they use the `PreallocationEscrow` contract as a proxy for the `StakingEscrow` and `PolicyManager` contracts.
## Contracts Versioning
Upgradeable contracts, such as `Adjudicator`, `StakingEscrow`, `PolicyManager` and `StakingInterface`, have their version specified in contract doc inside @dev.
Version format is `|vi.j.k|`, where `i` - major version, `j` - minor version, `k` - patch, for example `|v1.2.3|`:
* Different major versions mean different forks and they are not upgradeable
* Minor versions relate to any signatures or state changes inside contract, contracts are upgradeable between minor versions, but have different ABI and follows different agent layers
* Patches involve changes inside function(s) with signature(s) untouched. All patches with a common minor version can be upgraded from one to another without other changes

View File

@ -229,6 +229,7 @@ class ContractAdministrator(NucypherTokenActor):
gas_limit: int = None,
plaintext_secret: str = None,
bare: bool = False,
ignore_deployed: bool = False,
progress=None,
*args, **kwargs,
) -> Tuple[dict, BaseContractDeployer]:
@ -250,17 +251,24 @@ class ContractAdministrator(NucypherTokenActor):
receipts = deployer.deploy(secret_hash=secret_hash,
gas_limit=gas_limit,
initial_deployment=is_initial_deployment,
progress=progress)
progress=progress,
ignore_deployed=ignore_deployed)
else:
receipts = deployer.deploy(gas_limit=gas_limit, progress=progress)
return receipts, deployer
def upgrade_contract(self, contract_name: str, existing_plaintext_secret: str, new_plaintext_secret: str) -> dict:
def upgrade_contract(self,
contract_name: str,
existing_plaintext_secret: str,
new_plaintext_secret: str,
ignore_deployed: bool = False
) -> dict:
Deployer = self.__get_deployer(contract_name=contract_name)
deployer = Deployer(registry=self.registry, deployer_address=self.deployer_address)
new_secret_hash = keccak(bytes(new_plaintext_secret, encoding='utf-8'))
receipts = deployer.upgrade(existing_secret_plaintext=bytes(existing_plaintext_secret, encoding='utf-8'),
new_secret_hash=new_secret_hash)
new_secret_hash=new_secret_hash,
ignore_deployed=ignore_deployed)
return receipts
def retarget_proxy(self, contract_name: str, target_address: str, existing_plaintext_secret: str, new_plaintext_secret: str):
@ -293,13 +301,15 @@ class ContractAdministrator(NucypherTokenActor):
secrets: dict,
interactive: bool = True,
emitter: StdoutEmitter = None,
etherscan: bool = False) -> dict:
etherscan: bool = False,
ignore_deployed: bool = False) -> dict:
"""
:param secrets: Contract upgrade secrets dictionary
:param interactive: If True, wait for keypress after each contract deployment
:param emitter: A console output emitter instance. If emitter is None, no output will be echoed to the console.
:param etherscan: Open deployed contracts in Etherscan
:param ignore_deployed: Ignore already deployed contracts if exist
:return: Returns a dictionary of deployment receipts keyed by contract name
"""
@ -336,7 +346,8 @@ class ContractAdministrator(NucypherTokenActor):
receipts, deployer = self.deploy_contract(contract_name=deployer_class.contract_name,
plaintext_secret=secrets[deployer_class.contract_name],
gas_limit=gas_limit,
progress=bar)
progress=bar,
ignore_deployed=ignore_deployed)
if emitter:
blockchain = BlockchainInterfaceFactory.get_interface()

View File

@ -97,7 +97,7 @@ class EthereumContractAgent:
if contract is None: # Fetch the contract
contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=self.registry_contract_name,
contract_name=self.registry_contract_name,
proxy_name=self._proxy_name,
use_proxy_address=self._forward_address)
self.__contract = contract

View File

@ -34,7 +34,8 @@ from nucypher.blockchain.eth.agents import (
)
from nucypher.blockchain.eth.constants import DISPATCHER_CONTRACT_NAME
from nucypher.blockchain.eth.decorators import validate_secret, validate_checksum_address
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory, \
VersionedContract
from nucypher.blockchain.eth.registry import AllocationRegistry, BaseContractRegistry
@ -105,15 +106,25 @@ class BaseContractDeployer:
def dispatcher(self):
return self.__proxy_contract
@property
def is_deployed(self) -> bool:
return bool(self._contract is not CONTRACT_NOT_DEPLOYED)
@property
def ready_to_deploy(self) -> bool:
return bool(self.__ready_to_deploy is True)
def check_deployment_readiness(self, fail=True) -> Tuple[bool, list]:
def is_deployed(self, contract_version: str = None) -> bool:
try:
self.registry.search(contract_name=self.contract_name, contract_version=contract_version)
except BaseContractRegistry.UnknownContract:
return False
except BaseContractRegistry.NoRegistry:
return False
else:
return True
def check_deployment_readiness(self,
contract_version: str = None,
ignore_deployed=False,
fail=True
) -> Tuple[bool, list]:
"""
Iterates through a set of rules required for an ethereum
contract deployer to be eligible for deployment returning a
@ -128,8 +139,12 @@ class BaseContractDeployer:
if self.__ready_to_deploy is True:
return True, list()
if not ignore_deployed and contract_version is not None:
contract_version, _data = self.blockchain.find_raw_contract_data(contract_name=self.contract_name,
requested_version=contract_version)
rules = [
(self.is_deployed is not True, 'Contract already deployed'),
(ignore_deployed or self.is_deployed(contract_version) is not True, 'Contract already deployed'),
(self.deployer_address is not None, 'No deployer address set.'),
(self.deployer_address is not NO_DEPLOYER_CONFIGURED, 'No deployer address set.'),
]
@ -154,7 +169,7 @@ class BaseContractDeployer:
raise self.ContractDeploymentError(message)
return True
def deploy(self, gas_limit: int, progress) -> dict:
def deploy(self, gas_limit: int, progress, **overrides) -> dict:
"""
Provides for the setup, deployment, and initialization of ethereum smart contracts.
Emits the configured blockchain network transactions for single contract instance publication.
@ -165,12 +180,12 @@ class BaseContractDeployer:
agent = self.agency(registry=self.registry, contract=self._contract)
return agent
def get_latest_enrollment(self, registry: BaseContractRegistry) -> Contract:
def get_latest_enrollment(self, registry: BaseContractRegistry) -> VersionedContract:
"""Get the latest enrolled version of the contract from the registry."""
contract = self.blockchain.get_contract_by_name(name=self.contract_name,
contract = self.blockchain.get_contract_by_name(contract_name=self.contract_name,
registry=registry,
use_proxy_address=False,
version='latest')
enrollment_version='latest')
return contract
@ -223,7 +238,14 @@ class UpgradeableContractMixin:
class ContractNotUpgradeable(RuntimeError):
pass
def deploy(self, secret_hash: bytes, initial_deployment: bool = True, gas_limit: int = None, progress = None) -> dict:
def deploy(self,
secret_hash: bytes,
initial_deployment: bool = True,
gas_limit: int = None,
progress=None,
contract_version: str = "latest",
ignore_deployed: bool = False
) -> dict:
"""
Provides for the setup, deployment, and initialization of ethereum smart contracts.
Emits the configured blockchain network transactions for single contract instance publication.
@ -232,7 +254,7 @@ class UpgradeableContractMixin:
raise self.ContractNotUpgradeable(f"{self.contract_name} is not upgradeable.")
raise NotImplementedError
def get_principal_contract(self, registry: BaseContractRegistry, provider_uri: str = None) -> Contract:
def get_principal_contract(self, registry: BaseContractRegistry, provider_uri: str = None) -> VersionedContract:
"""
Get the on-chain targeted version of the principal contract directly
without assembling it with its proxy.
@ -240,13 +262,13 @@ class UpgradeableContractMixin:
if not self._upgradeable:
raise self.ContractNotUpgradeable(f"{self.contract_name} is not upgradeable.")
blockchain = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri)
principal_contract = blockchain.get_contract_by_name(name=self.contract_name,
principal_contract = blockchain.get_contract_by_name(contract_name=self.contract_name,
registry=registry,
proxy_name=self._proxy_deployer.contract_name,
use_proxy_address=False)
return principal_contract
def get_proxy_contract(self, registry: BaseContractRegistry, provider_uri: str = None) -> Contract:
def get_proxy_contract(self, registry: BaseContractRegistry, provider_uri: str = None) -> VersionedContract:
if not self._upgradeable:
raise cls.ContractNotUpgradeable(f"{self.contract_name} is not upgradeable.")
blockchain = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri)
@ -271,7 +293,8 @@ class UpgradeableContractMixin:
target_address: str,
existing_secret_plaintext: bytes,
new_secret_hash: bytes,
gas_limit: int = None):
gas_limit: int = None,
version: str = "latest"):
"""
Directly engage a proxy contract for an existing deployment, executing the proxy's
upgrade interfaces to verify upgradeability and modify the on-chain contract target.
@ -291,7 +314,13 @@ class UpgradeableContractMixin:
gas_limit=gas_limit)
return receipt
def upgrade(self, existing_secret_plaintext: bytes, new_secret_hash: bytes, gas_limit: int = None):
def upgrade(self,
existing_secret_plaintext: bytes,
new_secret_hash: bytes,
gas_limit: int = None,
contract_version: str = "latest",
ignore_deployed: bool = False,
**overrides):
"""
Deploy a new version of a contract, then engage the proxy contract's upgrade interfaces.
"""
@ -299,14 +328,16 @@ class UpgradeableContractMixin:
# 1 - Raise if not all-systems-go #
if not self._upgradeable:
raise self.ContractNotUpgradeable(f"{self.contract_name} is not upgradeable.")
self.check_deployment_readiness()
self.check_deployment_readiness(contract_version=contract_version, ignore_deployed=ignore_deployed)
# 2 - Get Bare Contracts
existing_bare_contract = self.get_principal_contract(registry=self.registry, provider_uri=self.blockchain.provider_uri)
proxy_deployer = self.get_proxy_deployer(registry=self.registry, provider_uri=self.blockchain.provider_uri)
# 3 - Deploy new version
new_contract, deploy_receipt = self._deploy_essential(gas_limit=gas_limit)
new_contract, deploy_receipt = self._deploy_essential(contract_version=contract_version,
gas_limit=gas_limit,
**overrides)
# 4 - Wrap the escrow contract
wrapped_contract = self.blockchain._wrap_contract(wrapper_contract=proxy_deployer.contract,
@ -333,7 +364,7 @@ class UpgradeableContractMixin:
raise self.ContractNotUpgradeable(f"{self.contract_name} is not upgradeable.")
existing_bare_contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=self.contract_name,
contract_name=self.contract_name,
proxy_name=self._proxy_deployer.contract_name,
use_proxy_address=False)
proxy_deployer = self.get_proxy_deployer(registry=self.registry, provider_uri=self.blockchain.provider_uri)
@ -361,7 +392,7 @@ class NucypherTokenDeployer(BaseContractDeployer):
_upgradeable = False
_ownable = False
def deploy(self, gas_limit: int = None, progress=None) -> dict:
def deploy(self, gas_limit: int = None, progress=None, **overrides) -> dict:
"""
Deploy and publish the NuCypher Token contract
to the blockchain network specified in self.blockchain.network.
@ -371,11 +402,14 @@ class NucypherTokenDeployer(BaseContractDeployer):
self.check_deployment_readiness()
# Order-sensitive!
constructor_kwargs = {"_totalSupply": self.economics.erc20_total_supply}
constructor_kwargs.update(overrides)
constructor_kwargs = {k: v for k, v in constructor_kwargs.items() if v is not None}
contract, deployment_receipt = self.blockchain.deploy_contract(self.deployer_address,
self.registry,
self.contract_name,
gas_limit=gas_limit,
_totalSupply=self.economics.erc20_total_supply)
**constructor_kwargs)
if progress:
progress.update(1)
self._contract = contract
@ -458,21 +492,36 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
token_contract_name = NucypherTokenDeployer.contract_name
self.token_contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=token_contract_name)
contract_name=token_contract_name)
def __check_policy_manager(self):
result = self.contract.functions.policyManager().call()
if result == self.blockchain.NULL_ADDRESS:
raise RuntimeError("PolicyManager contract is not initialized.")
def _deploy_essential(self, gas_limit: int = None):
escrow_constructor_args = (self.token_contract.address, *self.economics.staking_deployment_parameters)
def _deploy_essential(self, contract_version: str, gas_limit: int = None, **overrides):
args = self.economics.staking_deployment_parameters
constructor_kwargs = {
"_hoursPerPeriod": args[0],
"_miningCoefficient": args[1],
"_lockedPeriodsCoefficient": args[2],
"_rewardedPeriods": args[3],
"_minLockedPeriods": args[4],
"_minAllowableLockedTokens": args[5],
"_maxAllowableLockedTokens": args[6],
"_minWorkerPeriods": args[7]
}
constructor_kwargs.update(overrides)
constructor_kwargs = {k: v for k, v in constructor_kwargs.items() if v is not None}
# Force use of the token address from the registry
constructor_kwargs.update({"_token": self.token_contract.address})
the_escrow_contract, deploy_receipt = self.blockchain.deploy_contract(
self.deployer_address,
self.registry,
self.contract_name,
gas_limit=gas_limit,
*escrow_constructor_args,
contract_version=contract_version,
**constructor_kwargs
)
return the_escrow_contract, deploy_receipt
@ -481,7 +530,10 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
initial_deployment: bool = True,
secret_hash: bytes = None,
gas_limit: int = None,
progress=None
progress=None,
contract_version: str = "latest",
ignore_deployed: bool = False,
**overrides
) -> dict:
"""
Deploy and publish the StakingEscrow contract
@ -503,7 +555,7 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
f" deployment series for {self.contract_name}.")
# Raise if not all-systems-go
self.check_deployment_readiness()
self.check_deployment_readiness(contract_version=contract_version, ignore_deployed=ignore_deployed)
# Build deployment arguments
origin_args = {}
@ -511,7 +563,9 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
origin_args.update({'gas': gas_limit})
# 1 - Deploy #
the_escrow_contract, deploy_receipt = self._deploy_essential(gas_limit=gas_limit)
the_escrow_contract, deploy_receipt = self._deploy_essential(contract_version=contract_version,
gas_limit=gas_limit,
**overrides)
# This is the end of bare deployment.
if not initial_deployment:
@ -583,38 +637,42 @@ class PolicyManagerDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
deployment_steps = ('deployment', 'dispatcher_deployment', 'set_policy_manager')
_proxy_deployer = DispatcherDeployer
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
proxy_name = StakingEscrowDeployer._proxy_deployer.contract_name
staking_contract_name = StakingEscrowDeployer.contract_name
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=staking_contract_name,
contract_name=staking_contract_name,
proxy_name=proxy_name)
def _deploy_essential(self, gas_limit: int = None) -> tuple:
def _deploy_essential(self, contract_version: str, gas_limit: int = None) -> tuple:
constructor_kwargs = {"_escrow": self.staking_contract.address}
policy_manager_contract, deploy_receipt = self.blockchain.deploy_contract(self.deployer_address,
self.registry,
self.contract_name,
self.staking_contract.address,
gas_limit=gas_limit)
gas_limit=gas_limit,
contract_version=contract_version,
**constructor_kwargs)
return policy_manager_contract, deploy_receipt
def deploy(self,
initial_deployment: bool = True,
secret_hash: bytes = None,
gas_limit: int = None,
progress=None
progress=None,
contract_version: str = "latest",
ignore_deployed: bool = False
) -> Dict[str, dict]:
if initial_deployment and not secret_hash:
raise ValueError(f"An upgrade secret hash is required to perform an initial"
f" deployment series for {self.contract_name}.")
self.check_deployment_readiness()
self.check_deployment_readiness(contract_version=contract_version, ignore_deployed=ignore_deployed)
# Creator deploys the policy manager
policy_manager_contract, deploy_receipt = self._deploy_essential(gas_limit=gas_limit)
policy_manager_contract, deploy_receipt = self._deploy_essential(contract_version=contract_version,
gas_limit=gas_limit)
# This is the end of bare deployment.
if not initial_deployment:
@ -718,21 +776,21 @@ class StakingInterfaceDeployer(BaseContractDeployer, UpgradeableContractMixin):
token_contract_name = NucypherTokenDeployer.contract_name
self.token_contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=token_contract_name)
contract_name=token_contract_name)
staking_contract_name = StakingEscrowDeployer.contract_name
staking_proxy_name = StakingEscrowDeployer._proxy_deployer.contract_name
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=staking_contract_name,
contract_name=staking_contract_name,
proxy_name=staking_proxy_name)
policy_contract_name = PolicyManagerDeployer.contract_name
policy_proxy_name = PolicyManagerDeployer._proxy_deployer.contract_name
self.policy_contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=policy_contract_name,
contract_name=policy_contract_name,
proxy_name=policy_proxy_name)
def _deploy_essential(self, gas_limit: int = None):
def _deploy_essential(self, contract_version: str, gas_limit: int = None):
"""Note: These parameters are order-sensitive"""
constructor_args = (self.token_contract.address,
self.staking_contract.address,
@ -742,14 +800,17 @@ class StakingInterfaceDeployer(BaseContractDeployer, UpgradeableContractMixin):
self.registry,
self.contract_name,
*constructor_args,
gas_limit=gas_limit)
gas_limit=gas_limit,
contract_version=contract_version)
return contract, deployment_receipt
def deploy(self,
initial_deployment: bool = True,
secret_hash: bytes = None,
gas_limit: int = None,
progress=None
progress=None,
contract_version: str = "latest",
ignore_deployed: bool = False
) -> dict:
"""
Deploys a new StakingInterface contract, and a new StakingInterfaceRouter, targeting the former.
@ -759,9 +820,11 @@ class StakingInterfaceDeployer(BaseContractDeployer, UpgradeableContractMixin):
if initial_deployment and not secret_hash:
raise ValueError(f"An upgrade secret hash is required to perform an initial"
f" deployment series for {self.contract_name}.")
self.check_deployment_readiness(contract_version=contract_version, ignore_deployed=ignore_deployed)
# 1 - StakingInterface
staking_interface_contract, deployment_receipt = self._deploy_essential(gas_limit=gas_limit)
staking_interface_contract, deployment_receipt = self._deploy_essential(contract_version=contract_version,
gas_limit=gas_limit)
# This is the end of bare deployment.
if not initial_deployment:
@ -802,10 +865,10 @@ class PreallocationEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin
def __init__(self, allocation_registry: AllocationRegistry = None, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.token_contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=NucypherTokenDeployer.contract_name)
contract_name=NucypherTokenDeployer.contract_name)
dispatcher_name = StakingEscrowDeployer._proxy_deployer.contract_name
self.staking_escrow_contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=StakingEscrowDeployer.contract_name,
contract_name=StakingEscrowDeployer.contract_name,
proxy_name=dispatcher_name)
self.__beneficiary_address = NO_BENEFICIARY
self.__allocation_registry = allocation_registry or self.__allocation_registry()
@ -887,7 +950,7 @@ class PreallocationEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin
"""Deploy a new instance of PreallocationEscrow to the blockchain."""
self.check_deployment_readiness()
router_contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=self._router_deployer.contract_name)
contract_name=self._router_deployer.contract_name)
args = (self.deployer_address,
self.registry,
self.contract_name,
@ -923,32 +986,48 @@ class AdjudicatorDeployer(BaseContractDeployer, UpgradeableContractMixin, Ownabl
staking_contract_name = StakingEscrowDeployer.contract_name
proxy_name = StakingEscrowDeployer._proxy_deployer.contract_name
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
name=staking_contract_name,
contract_name=staking_contract_name,
proxy_name=proxy_name)
def _deploy_essential(self, gas_limit: int = None):
constructor_args = (self.staking_contract.address,
*self.economics.slashing_deployment_parameters)
def _deploy_essential(self, contract_version: str, gas_limit: int = None, **overrides):
args = self.economics.slashing_deployment_parameters
constructor_kwargs = {
"_hashAlgorithm": args[0],
"_basePenalty": args[1],
"_penaltyHistoryCoefficient": args[2],
"_percentagePenaltyCoefficient": args[3],
"_rewardCoefficient": args[4]
}
constructor_kwargs.update(overrides)
constructor_kwargs = {k: v for k, v in constructor_kwargs.items() if v is not None}
# Force use of the escrow address from the registry
constructor_kwargs.update({"_escrow": self.staking_contract.address})
adjudicator_contract, deploy_receipt = self.blockchain.deploy_contract(self.deployer_address,
self.registry,
self.contract_name,
*constructor_args,
gas_limit=gas_limit)
gas_limit=gas_limit,
contract_version=contract_version,
**constructor_kwargs)
return adjudicator_contract, deploy_receipt
def deploy(self,
initial_deployment: bool = True,
secret_hash: bytes = None,
gas_limit: int = None,
progress=None) -> Dict[str, str]:
progress=None,
contract_version: str = "latest",
ignore_deployed: bool = False,
**overrides) -> Dict[str, str]:
if initial_deployment and not secret_hash:
raise ValueError(f"An upgrade secret hash is required to perform an initial"
f" deployment series for {self.contract_name}.")
self.check_deployment_readiness()
self.check_deployment_readiness(contract_version=contract_version, ignore_deployed=ignore_deployed)
adjudicator_contract, deploy_receipt = self._deploy_essential(gas_limit=gas_limit)
adjudicator_contract, deploy_receipt = self._deploy_essential(contract_version=contract_version,
gas_limit=gas_limit,
**overrides)
# This is the end of bare deployment.
if not initial_deployment:

View File

@ -37,8 +37,7 @@ from eth_tester import EthereumTester
from eth_utils import to_checksum_address, is_checksum_address
from twisted.logger import Logger
from web3 import Web3, WebsocketProvider, HTTPProvider, IPCProvider
from web3.contract import Contract
from web3.contract import ContractConstructor
from web3.contract import ContractConstructor, Contract
from web3.contract import ContractFunction
from web3.exceptions import TimeExhausted
from web3.exceptions import ValidationError
@ -59,11 +58,14 @@ from nucypher.blockchain.eth.providers import (
from nucypher.blockchain.eth.registry import BaseContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.utilities.logging import console_observer, GlobalLoggerSettings
Web3Providers = Union[IPCProvider, WebsocketProvider, HTTPProvider, EthereumTester]
class VersionedContract(Contract):
version = None
class BlockchainInterface:
"""
Interacts with a solidity compiler and a registry in order to instantiate compiled
@ -76,7 +78,7 @@ class BlockchainInterface:
process = NO_PROVIDER_PROCESS.bool_value(False)
Web3 = Web3
_contract_factory = Contract
_contract_factory = VersionedContract
class InterfaceError(Exception):
pass
@ -431,20 +433,21 @@ class BlockchainInterface:
def get_contract_by_name(self,
registry: BaseContractRegistry,
name: str,
version: int = None,
contract_name: str,
contract_version: str = None,
enrollment_version: Union[int, str] = None,
proxy_name: str = None,
use_proxy_address: bool = True
) -> Union[Contract, List[tuple]]:
) -> Union[VersionedContract, List[tuple]]:
"""
Instantiate a deployed contract from registry data,
and assimilate it with its proxy if it is upgradeable,
or return all registered records if use_proxy_address is False.
"""
target_contract_records = registry.search(contract_name=name)
target_contract_records = registry.search(contract_name=contract_name, contract_version=contract_version)
if not target_contract_records:
raise self.UnknownContract(f"No such contract records with name {name}.")
raise self.UnknownContract(f"No such contract records with name {contract_name}:{contract_version}.")
if proxy_name:
@ -452,61 +455,69 @@ class BlockchainInterface:
proxy_records = registry.search(contract_name=proxy_name)
results = list()
for proxy_name, proxy_addr, proxy_abi in proxy_records:
for proxy_name, proxy_version, proxy_address, proxy_abi in proxy_records:
proxy_contract = self.client.w3.eth.contract(abi=proxy_abi,
address=proxy_addr,
address=proxy_address,
version=proxy_version,
ContractFactoryClass=self._contract_factory)
# Read this dispatcher's target address from the blockchain
proxy_live_target_address = proxy_contract.functions.target().call()
for target_name, target_addr, target_abi in target_contract_records:
for target_name, target_version, target_address, target_abi in target_contract_records:
if target_addr == proxy_live_target_address:
if target_address == proxy_live_target_address:
if use_proxy_address:
pair = (proxy_addr, target_abi)
triplet = (proxy_address, target_version, target_abi)
else:
pair = (target_addr, target_abi)
triplet = (target_address, target_version, target_abi)
else:
continue
results.append(pair)
results.append(triplet)
if len(results) > 1:
address, abi = results[0]
address, _version, _abi = results[0]
message = "Multiple {} deployments are targeting {}".format(proxy_name, address)
raise self.InterfaceError(message.format(name))
raise self.InterfaceError(message.format(contract_name))
else:
try:
selected_address, selected_abi = results[0]
selected_address, selected_version, selected_abi = results[0]
except IndexError:
raise self.UnknownContract(f"There are no Dispatcher records targeting '{name}'")
raise self.UnknownContract(
f"There are no Dispatcher records targeting '{contract_name}':{contract_version}")
else:
# NOTE: 0 must be allowed as a valid version number
if len(target_contract_records) != 1:
if version is None:
m = f"{len(target_contract_records)} records enrolled for contract {name} " \
if enrollment_version is None:
m = f"{len(target_contract_records)} records enrolled " \
f"for contract {contract_name}:{contract_version} " \
f"and no version index was supplied."
raise self.InterfaceError(m)
version = self.__get_version_index(name=name,
version_index=version,
enrollments=len(target_contract_records))
enrollment_version = self.__get_enrollment_version_index(name=contract_name,
contract_version=contract_version,
version_index=enrollment_version,
enrollments=len(target_contract_records))
else:
version = -1 # default
enrollment_version = -1 # default
_target_contract_name, selected_address, selected_abi = target_contract_records[version]
_contract_name, selected_version, selected_address, selected_abi = target_contract_records[enrollment_version]
# Create the contract from selected sources
unified_contract = self.client.w3.eth.contract(abi=selected_abi,
address=selected_address,
version=selected_version,
ContractFactoryClass=self._contract_factory)
return unified_contract
@staticmethod
def __get_version_index(version_index: Union[int, str], enrollments: int, name: str):
def __get_enrollment_version_index(version_index: Union[int, str],
enrollments: int,
name: str,
contract_version: str):
version_names = {'latest': -1, 'earliest': 0}
try:
version = version_names[version_index]
@ -515,10 +526,11 @@ class BlockchainInterface:
version = int(version_index)
except ValueError:
what_is_this = version_index
raise ValueError(f"'{what_is_this}' is not a valid version number")
raise ValueError(f"'{what_is_this}' is not a valid enrollment version number")
else:
if version > enrollments - 1:
message = f"Version index '{version}' is larger than the number of enrollments for {name}."
message = f"Version index '{version}' is larger than the number of enrollments " \
f"for {name}:{contract_version}."
raise ValueError(message)
return version
@ -526,7 +538,7 @@ class BlockchainInterface:
class BlockchainDeployerInterface(BlockchainInterface):
TIMEOUT = 600 # seconds
_contract_factory = Contract
_contract_factory = VersionedContract
class NoDeployerAddress(RuntimeError):
pass
@ -544,18 +556,13 @@ class BlockchainDeployerInterface(BlockchainInterface):
return self.is_connected
def _setup_solidity(self, compiler: SolidityCompiler = None):
# if a SolidityCompiler class instance was passed,
# compile from solidity source code.
self.__sol_compiler = compiler
if compiler:
# Execute the compilation if we're recompiling
# Otherwise read compiled contract data from the registry.
interfaces = self.__sol_compiler.compile()
__raw_contract_cache = interfaces
_raw_contract_cache = compiler.compile()
else:
__raw_contract_cache = NO_COMPILATION_PERFORMED
self.__raw_contract_cache = __raw_contract_cache
_raw_contract_cache = NO_COMPILATION_PERFORMED
self._raw_contract_cache = _raw_contract_cache
@validate_checksum_address
def deploy_contract(self,
@ -565,8 +572,9 @@ class BlockchainDeployerInterface(BlockchainInterface):
*constructor_args,
enroll: bool = True,
gas_limit: int = None,
contract_version: str = 'latest',
**constructor_kwargs
) -> Tuple[Contract, dict]:
) -> Tuple[VersionedContract, dict]:
"""
Retrieve compiled interface data from the cache and
return an instantiated deployed contract
@ -585,11 +593,12 @@ class BlockchainDeployerInterface(BlockchainInterface):
pprint_args = str(tuple(constructor_args))
pprint_args = pprint_args.replace("{", "{{").replace("}", "}}") # See #724
self.log.info(f"Deploying contract {contract_name} with "
contract_factory = self.get_contract_factory(contract_name=contract_name, version=contract_version)
self.log.info(f"Deploying contract {contract_name}:{contract_factory.version} with "
f"deployer address {deployer_address} "
f"and parameters {pprint_args}")
contract_factory = self.get_contract_factory(contract_name=contract_name)
transaction_function = contract_factory.constructor(*constructor_args, **constructor_kwargs)
#
@ -606,39 +615,74 @@ class BlockchainDeployerInterface(BlockchainInterface):
# Success
address = receipt['contractAddress']
self.log.info(f"Confirmed {contract_name} deployment: new address {address}")
self.log.info(f"Confirmed {contract_name}:{contract_factory.version} deployment: new address {address}")
#
# Instantiate & Enroll contract
#
contract = self.client.w3.eth.contract(address=address, abi=contract_factory.abi)
contract = self.client.w3.eth.contract(address=address,
abi=contract_factory.abi,
version=contract_factory.version,
ContractFactoryClass=self._contract_factory)
if enroll is True:
registry.enroll(contract_name=contract_name,
contract_address=contract.address,
contract_abi=contract_factory.abi)
contract_abi=contract.abi,
contract_version=contract.version)
return contract, receipt # receipt
def get_contract_factory(self, contract_name: str) -> Contract:
"""Retrieve compiled interface data from the cache and return web3 contract"""
def find_raw_contract_data(self, contract_name: str, requested_version: str = 'latest') -> Tuple[str, dict]:
try:
interface = self.__raw_contract_cache[contract_name]
contract_data = self._raw_contract_cache[contract_name]
except KeyError:
raise self.UnknownContract('{} is not a locally compiled contract.'.format(contract_name))
except TypeError:
if self.__raw_contract_cache is NO_COMPILATION_PERFORMED:
if self._raw_contract_cache is NO_COMPILATION_PERFORMED:
message = "The local contract compiler cache is empty because no compilation was performed."
raise self.InterfaceError(message)
raise
else:
contract = self.client.w3.eth.contract(abi=interface['abi'],
bytecode=interface['bin'],
ContractFactoryClass=Contract)
return contract
def _wrap_contract(self, wrapper_contract: Contract, target_contract: Contract) -> Contract:
try:
return requested_version, contract_data[requested_version]
except KeyError:
if requested_version != 'latest' and requested_version != 'earliest':
raise self.UnknownContract('Version {} of contract {} is not a locally compiled. '
'Available versions: {}'
.format(requested_version, contract_name, contract_data.keys()))
if len(contract_data.keys()) == 1:
return next(iter(contract_data.items()))
# Get the latest or the earliest versions
current_version_parsed = (-1, -1, -1)
current_version = None
current_data = None
for version, data in contract_data.items():
major, minor, patch = [int(v) for v in version[1:].split(".", 3)]
if current_version_parsed[0] == -1 or \
requested_version == 'latest' and (major, minor, patch) > current_version_parsed or \
requested_version == 'earliest' and (major, minor, patch) < current_version_parsed:
current_version_parsed = (major, minor, patch)
current_data = data
current_version = version
return current_version, current_data
def get_contract_factory(self, contract_name: str, version: str = 'latest') -> VersionedContract:
"""Retrieve compiled interface data from the cache and return web3 contract"""
version, interface = self.find_raw_contract_data(contract_name, version)
contract = self.client.w3.eth.contract(abi=interface['abi'],
bytecode=interface['bin'],
version=version,
ContractFactoryClass=self._contract_factory)
return contract
def _wrap_contract(self,
wrapper_contract: VersionedContract,
target_contract: VersionedContract
) -> VersionedContract:
"""
Used for upgradeable contracts; Returns a new contract object assembled
with its own address but the abi of the other.
@ -647,6 +691,7 @@ class BlockchainDeployerInterface(BlockchainInterface):
# Wrap the contract
wrapped_contract = self.client.w3.eth.contract(abi=target_contract.abi,
address=wrapper_contract.address,
version=target_contract.version,
ContractFactoryClass=self._contract_factory)
return wrapped_contract
@ -654,15 +699,16 @@ class BlockchainDeployerInterface(BlockchainInterface):
def get_proxy_contract(self,
registry: BaseContractRegistry,
target_address: str,
proxy_name: str) -> Contract:
proxy_name: str) -> VersionedContract:
# Lookup proxies; Search for a registered proxy that targets this contract record
records = registry.search(contract_name=proxy_name)
dispatchers = list()
for name, addr, abi in records:
for name, version, address, abi in records:
proxy_contract = self.client.w3.eth.contract(abi=abi,
address=addr,
address=address,
version=version,
ContractFactoryClass=self._contract_factory)
# Read this dispatchers target address from the blockchain

View File

@ -282,7 +282,7 @@ class BaseContractRegistry(ABC):
entries = iter(record[1] for record in self.read())
return entries
def enroll(self, contract_name, contract_address, contract_abi) -> None:
def enroll(self, contract_name, contract_address, contract_abi, contract_version) -> None:
"""
Enrolls a contract to the chain registry by writing the name, address,
and abi information to the filesystem as JSON.
@ -290,32 +290,42 @@ class BaseContractRegistry(ABC):
Note: Unless you are developing NuCypher, you most likely won't ever
need to use this.
"""
contract_data = [contract_name, contract_address, contract_abi]
contract_data = [contract_name, contract_version, contract_address, contract_abi]
try:
registry_data = self.read()
except self.RegistryError:
self.log.info("Blank registry encountered: enrolling {}:{}".format(contract_name, contract_address))
self.log.info("Blank registry encountered: enrolling {}:{}:{}"
.format(contract_name, contract_version, contract_address))
registry_data = list() # empty registry
registry_data.append(contract_data)
self.write(registry_data)
self.log.info("Enrolled {}:{} into registry.".format(contract_name, contract_address))
self.log.info("Enrolled {}:{}:{} into registry.".format(contract_name, contract_version, contract_address))
def search(self, contract_name: str = None, contract_address: str = None) -> tuple:
def search(self, contract_name: str = None, contract_version: str = None, contract_address: str = None) -> tuple:
"""
Searches the registry for a contract with the provided name or address
and returns the contracts component data.
"""
if not (bool(contract_name) ^ bool(contract_address)):
raise ValueError("Pass contract_name or contract_address, not both.")
if bool(contract_version) and not bool(contract_name):
raise ValueError("Pass contract_version together with contract_name.")
contracts = list()
registry_data = self.read()
try:
for name, addr, abi in registry_data:
if contract_name == name or contract_address == addr:
contracts.append((name, addr, abi))
for contract in registry_data:
if len(contract) == 3:
name, address, abi = contract
version = None
else:
name, version, address, abi = contract
if contract_name == name and \
(contract_version is None or version == contract_version) or \
contract_address == address:
contracts.append((name, version, address, abi))
except ValueError:
message = "Missing or corrupted registry data"
self.log.critical(message)

View File

@ -14,9 +14,10 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import collections
import os
import re
from typing import List, Set, Tuple
import sys
from twisted.logger import Logger
@ -32,10 +33,16 @@ except ImportError:
# TODO: Issue #461 and #758 & PR #1480 - Include precompiled ABI; Do not use py-solc in standard installation
pass
SourceDirs = collections.namedtuple('SourceDirs', ['root_source_dir', # type: str
'other_source_dirs', # type: Set[str]
])
SourceDirs.__new__.__defaults__ = (None,)
class SolidityCompiler:
__default_version = 'v0.5.9'
__default_compiler_version = 'v0.5.9'
__default_contract_version = 'v0.0.0'
__default_configuration_path = os.path.join(dirname(abspath(__file__)), './compiler.json')
__default_sol_binary_path = shutil.which('solc')
@ -43,24 +50,35 @@ class SolidityCompiler:
__bin_path = os.path.dirname(sys.executable) # type: str
__default_sol_binary_path = os.path.join(__bin_path, 'solc') # type: str
__default_contract_dir = os.path.join(dirname(abspath(__file__)), 'source', 'contracts')
__default_contract_dir = os.path.join(dirname(abspath(__file__)), 'source')
__default_chain_name = 'tester'
__compiled_contracts_dir = 'contracts'
__zeppelin_library_dir = 'zeppelin'
optimization_runs = 200
class CompilerError(Exception):
pass
@classmethod
def default_contract_dir(cls):
return cls.__default_contract_dir
def __init__(self,
solc_binary_path: str = None,
configuration_path: str = None,
chain_name: str = None,
source_dir: str = None,
test_contract_dir: str = None
source_dirs: List[SourceDirs] = None
) -> None:
self.log = Logger('solidity-compiler')
# Compiler binary and root solidity source code directory
self.__sol_binary_path = solc_binary_path if solc_binary_path is not None else self.__default_sol_binary_path
self.source_dir = source_dir if source_dir is not None else self.__default_contract_dir
self._test_solidity_source_dir = test_contract_dir
if source_dirs is None or len(source_dirs) == 0:
self.source_dirs = [SourceDirs(root_source_dir=self.__default_contract_dir)]
else:
self.source_dirs = source_dirs
# JSON config
self.__configuration_path = configuration_path if configuration_path is not None else self.__default_configuration_path
@ -74,20 +92,58 @@ class SolidityCompiler:
Installs the specified solidity compiler version.
https://github.com/ethereum/py-solc#installing-the-solc-binary
"""
version = version if version is not None else self.__default_version
version = version if version is not None else self.__default_compiler_version
return install_solc(version, platform=None) # TODO: #1478 - Implement or remove this
def compile(self) -> dict:
interfaces = dict()
for root_source_dir, other_source_dirs in self.source_dirs:
if root_source_dir is None:
self.log.warn("One of the root directories is None")
continue
raw_interfaces = self._compile(root_source_dir, other_source_dirs)
for name, data in raw_interfaces.items():
# Extract contract version from docs
version_search = re.search(r"""
\"details\": # @dev tag in contract docs
\".*? # Skip any data in the beginning of details
\| # Beginning of version definition |
(v # Capture version starting from symbol v
\d+ # At least one digit of major version
\. # Digits splitter
\d+ # At least one digit of minor version
\. # Digits splitter
\d+ # At least one digit of patch
) # End of capturing
\| # End of version definition |
.*?\" # Skip any data in the end of details
""", data['devdoc'], re.VERBOSE)
version = version_search.group(1) if version_search else self.__default_contract_version
try:
existence_data = interfaces[name]
except KeyError:
existence_data = dict()
interfaces.update({name: existence_data})
if version not in existence_data:
existence_data.update({version: data})
return interfaces
def _compile(self, root_source_dir: str, other_source_dirs: [str]) -> dict:
"""Executes the compiler with parameters specified in the json config"""
self.log.info("Using solidity compiler binary at {}".format(self.__sol_binary_path))
self.log.info("Compiling solidity source files at {}".format(self.source_dir))
contracts_dir = os.path.join(root_source_dir, self.__compiled_contracts_dir)
self.log.info("Compiling solidity source files at {}".format(contracts_dir))
source_paths = set()
source_walker = os.walk(top=self.source_dir, topdown=True)
if self._test_solidity_source_dir:
test_source_walker = os.walk(top=self._test_solidity_source_dir, topdown=True)
source_walker = itertools.chain(source_walker, test_source_walker)
source_walker = os.walk(top=contracts_dir, topdown=True)
if other_source_dirs is not None:
for source_dir in other_source_dirs:
other_source_walker = os.walk(top=source_dir, topdown=True)
source_walker = itertools.chain(source_walker, other_source_walker)
for root, dirs, files in source_walker:
for filename in files:
@ -97,10 +153,10 @@ class SolidityCompiler:
self.log.debug("Collecting solidity source {}".format(path))
# Compile with remappings: https://github.com/ethereum/py-solc
project_root = dirname(self.source_dir)
zeppelin_dir = os.path.join(root_source_dir, self.__zeppelin_library_dir)
remappings = ("contracts={}".format(self.source_dir),
"zeppelin={}".format(os.path.join(project_root, 'zeppelin')),
remappings = ("contracts={}".format(contracts_dir),
"zeppelin={}".format(zeppelin_dir),
)
self.log.info("Compiling with import remappings {}".format(", ".join(remappings)))
@ -109,7 +165,7 @@ class SolidityCompiler:
try:
compiled_sol = compile_files(source_files=source_paths,
import_remappings=remappings,
allow_paths=project_root,
allow_paths=root_source_dir,
optimize=True,
optimize_runs=optimization_runs)

View File

@ -10,7 +10,8 @@ import "zeppelin/math/Math.sol";
/**
* @notice Supervises stakers' behavior and punishes when something's wrong.
**/
* @dev |v1.1.1|
*/
contract Adjudicator is Upgradeable {
using SafeMath for uint256;
@ -47,7 +48,7 @@ contract Adjudicator is Upgradeable {
* @param _penaltyHistoryCoefficient Coefficient for calculating the penalty depending on the history
* @param _percentagePenaltyCoefficient Coefficient for calculating the percentage penalty
* @param _rewardCoefficient Coefficient for calculating the reward
**/
*/
constructor(
StakingEscrow _escrow,
SignatureVerifier.HashAlgorithm _hashAlgorithm,
@ -81,7 +82,7 @@ contract Adjudicator is Upgradeable {
* @param _workerPublicKey Worker's signing public key, also known as "stamp"
* @param _workerIdentityEvidence Signature of worker's public key by worker's eth-key
* @param _preComputedData Additional pre-computed data for CFrag correctness verification
**/
*/
function evaluateCFrag(
bytes memory _capsuleBytes,
bytes memory _cFragBytes,
@ -174,7 +175,7 @@ contract Adjudicator is Upgradeable {
* @notice Calculate penalty to the staker and reward to the investigator
* @param _staker Staker's address
* @param _stakerValue Amount of tokens that belong to the staker
**/
*/
function calculatePenaltyAndReward(address _staker, uint256 _stakerValue)
internal returns (uint256 penalty, uint256 reward)
{

View File

@ -10,7 +10,8 @@ import "contracts/lib/AdditionalMath.sol";
/**
* @notice Contract for calculate issued tokens
**/
* @dev |v1.1.2|
*/
contract Issuer is Upgradeable {
using SafeMath for uint256;
using AdditionalMath for uint32;
@ -32,7 +33,7 @@ contract Issuer is Upgradeable {
* supply for previous period (used in formula) and supply for current period which accumulates value
* before end of period. There is no order between them because of storage savings.
* So each time should check values of both variables.
**/
*/
uint256 public currentSupply1;
uint256 public currentSupply2;
@ -46,7 +47,7 @@ contract Issuer is Upgradeable {
* @param _miningCoefficient Mining coefficient (k2)
* @param _lockedPeriodsCoefficient Locked periods coefficient (k1)
* @param _rewardedPeriods Max periods that will be additionally rewarded
**/
*/
constructor(
NuCypherToken _token,
uint32 _hoursPerPeriod,
@ -79,7 +80,7 @@ contract Issuer is Upgradeable {
/**
* @dev Checks contract initialization
**/
*/
modifier isInitialized()
{
require(currentSupply1 != 0);
@ -88,14 +89,14 @@ contract Issuer is Upgradeable {
/**
* @return Number of current period
**/
*/
function getCurrentPeriod() public view returns (uint16) {
return uint16(block.timestamp / secondsPerPeriod);
}
/**
* @notice Initialize reserved tokens for reward
**/
*/
function initialize() public onlyOwner {
require(currentSupply1 == 0);
currentMintingPeriod = getCurrentPeriod();
@ -162,7 +163,7 @@ contract Issuer is Upgradeable {
/**
* @notice Return tokens for future minting
* @param _amount Amount of tokens
**/
*/
function unMint(uint256 _amount) internal {
currentSupply1 = currentSupply1 - _amount;
currentSupply2 = currentSupply2 - _amount;
@ -170,7 +171,7 @@ contract Issuer is Upgradeable {
/**
* @notice Returns the number of tokens that can be mined
**/
*/
function getReservedReward() public view returns (uint256) {
return totalSupply - Math.max(currentSupply1, currentSupply2);
}

View File

@ -6,7 +6,7 @@ import "zeppelin/math/SafeMath.sol";
/**
* @notice Multi-signature contract with off-chain signing
**/
*/
contract MultiSig {
using SafeMath for uint256;
@ -33,7 +33,7 @@ contract MultiSig {
/**
* @param _required Number of required signings
* @param _owners List of initial owners.
**/
*/
constructor (uint8 _required, address[] memory _owners) public {
require(_owners.length <= MAX_OWNER_COUNT &&
_required <= _owners.length &&
@ -56,7 +56,7 @@ contract MultiSig {
* @param _value Amount of ETH to transfer
* @param _data Call data
* @param _nonce Nonce
**/
*/
function getUnsignedTransactionHash(
address _sender,
address _destination,
@ -78,7 +78,7 @@ contract MultiSig {
* @param _destination Destination address
* @param _value Amount of ETH to transfer
* @param _data Call data
**/
*/
function execute(
uint8[] calldata _sigV,
bytes32[] calldata _sigR,
@ -111,7 +111,7 @@ contract MultiSig {
* @notice Allows to add a new owner
* @dev Transaction has to be sent by `execute` method.
* @param _owner Address of new owner
**/
*/
function addOwner(address _owner)
public
onlyThisContract
@ -128,7 +128,7 @@ contract MultiSig {
* @notice Allows to remove an owner
* @dev Transaction has to be sent by `execute` method.
* @param _owner Address of owner
**/
*/
function removeOwner(address _owner)
public
onlyThisContract
@ -149,7 +149,7 @@ contract MultiSig {
* @notice Allows to change the number of required signings
* @dev Transaction has to be sent by `execute` method
* @param _required Number of required signings
**/
*/
function changeRequirement(uint8 _required)
public
onlyThisContract

View File

@ -9,13 +9,13 @@ import "zeppelin/token/ERC20/ERC20Detailed.sol";
* @title NuCypher token
* @notice ERC20 token
* @dev Optional approveAndCall() functionality to notify a contract if an approve() has occurred.
**/
*/
contract NuCypherToken is ERC20, ERC20Detailed('NuCypher', 'NU', 18) {
/**
* @notice Set amount of tokens
* @param _totalSupply Total number of tokens
**/
*/
constructor (uint256 _totalSupply) public {
_mint(msg.sender, _totalSupply);
}
@ -25,7 +25,7 @@ contract NuCypherToken is ERC20, ERC20Detailed('NuCypher', 'NU', 18) {
*
* @dev call the receiveApproval function on the contract you want to be notified.
* receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData)
**/
*/
function approveAndCall(address _spender, uint256 _value, bytes memory _extraData)
public returns (bool success)
{
@ -39,7 +39,7 @@ contract NuCypherToken is ERC20, ERC20Detailed('NuCypher', 'NU', 18) {
/**
* @dev Interface to use the receiveApproval method
**/
*/
contract TokenRecipient {
/**
@ -48,7 +48,7 @@ contract TokenRecipient {
* @param _value The amount of tokens to be spent
* @param _tokenContract Address of the token contract
* @param _extraData Extra data
**/
*/
function receiveApproval(address _from, uint256 _value, address _tokenContract, bytes calldata _extraData) external;
}

View File

@ -13,7 +13,8 @@ import "contracts/proxy/Upgradeable.sol";
/**
* @notice Contract holds policy data and locks fees
**/
* @dev |v1.1.2|
*/
contract PolicyManager is Upgradeable {
using SafeERC20 for NuCypherToken;
using SafeMath for uint256;
@ -92,7 +93,7 @@ contract PolicyManager is Upgradeable {
/**
* @notice Constructor sets address of the escrow contract
* @param _escrow Escrow contract
**/
*/
constructor(StakingEscrow _escrow) public {
// if the input address is not the StakingEscrow then calling `secondsPerPeriod` will throw error
secondsPerPeriod = _escrow.secondsPerPeriod();
@ -102,7 +103,7 @@ contract PolicyManager is Upgradeable {
/**
* @dev Checks that sender is the StakingEscrow contract
**/
*/
modifier onlyEscrowContract()
{
require(msg.sender == address(escrow));
@ -111,7 +112,7 @@ contract PolicyManager is Upgradeable {
/**
* @return Number of current period
**/
*/
function getCurrentPeriod() public view returns (uint16) {
return uint16(block.timestamp / secondsPerPeriod);
}
@ -120,7 +121,7 @@ contract PolicyManager is Upgradeable {
* @notice Register a node
* @param _node Node address
* @param _period Initial period
**/
*/
function register(address _node, uint16 _period) external onlyEscrowContract {
NodeInfo storage nodeInfo = nodes[_node];
require(nodeInfo.lastMinedPeriod == 0);
@ -129,7 +130,7 @@ contract PolicyManager is Upgradeable {
/**
* @notice Set the minimum reward that the node will take
**/
*/
function setMinRewardRate(uint256 _minRewardRate) public {
NodeInfo storage node = nodes[msg.sender];
node.minRewardRate = _minRewardRate;
@ -143,7 +144,7 @@ contract PolicyManager is Upgradeable {
* @param _numberOfPeriods Duration of the policy in periods except first period
* @param _firstPartialReward Partial reward for first/current period
* @param _nodes Nodes that will handle policy
**/
*/
function createPolicy(
bytes16 _policyId,
uint16 _numberOfPeriods,
@ -189,7 +190,7 @@ contract PolicyManager is Upgradeable {
* @notice Update node reward
* @param _node Node address
* @param _period Processed period
**/
*/
function updateReward(address _node, uint16 _period) external onlyEscrowContract {
NodeInfo storage node = nodes[_node];
if (node.lastMinedPeriod == 0 || _period <= node.lastMinedPeriod) {
@ -204,7 +205,7 @@ contract PolicyManager is Upgradeable {
/**
* @notice Withdraw reward by node
**/
*/
function withdraw() public returns (uint256) {
return withdraw(msg.sender);
}
@ -212,7 +213,7 @@ contract PolicyManager is Upgradeable {
/**
* @notice Withdraw reward by node
* @param _recipient Recipient of the reward
**/
*/
function withdraw(address payable _recipient) public returns (uint256) {
NodeInfo storage node = nodes[msg.sender];
uint256 reward = node.reward;
@ -227,7 +228,7 @@ contract PolicyManager is Upgradeable {
* @notice Calculate amount of refund
* @param _policy Policy
* @param _arrangement Arrangement
**/
*/
function calculateRefundValue(Policy storage _policy, ArrangementInfo storage _arrangement)
internal view returns (uint256 refundValue, uint256 indexOfDowntimePeriods, uint16 lastRefundedPeriod)
{
@ -288,7 +289,7 @@ contract PolicyManager is Upgradeable {
* @param _policyId Policy id
* @param _node Node that will be excluded or RESERVED_NODE if full policy should be used
( @param _forceRevoke Force revoke arrangement/policy
**/
*/
function refundInternal(bytes16 _policyId, address _node, bool _forceRevoke)
internal returns (uint256 refundValue)
{
@ -348,7 +349,7 @@ contract PolicyManager is Upgradeable {
* @notice Calculate amount of refund
* @param _policyId Policy id
* @param _node Node or RESERVED_NODE if all nodes should be used
**/
*/
function calculateRefundValueInternal(bytes16 _policyId, address _node)
internal view returns (uint256 refundValue)
{
@ -375,7 +376,7 @@ contract PolicyManager is Upgradeable {
/**
* @notice Revoke policy by client
* @param _policyId Policy id
**/
*/
function revokePolicy(bytes16 _policyId) public {
refundInternal(_policyId, RESERVED_NODE, true);
}
@ -384,7 +385,7 @@ contract PolicyManager is Upgradeable {
* @notice Revoke arrangement by client
* @param _policyId Policy id
* @param _node Node that will be excluded
**/
*/
function revokeArrangement(bytes16 _policyId, address _node)
public returns (uint256 refundValue)
{
@ -395,7 +396,7 @@ contract PolicyManager is Upgradeable {
/**
* @notice Refund part of fee by client
* @param _policyId Policy id
**/
*/
function refund(bytes16 _policyId) public {
refundInternal(_policyId, RESERVED_NODE, false);
}
@ -404,7 +405,7 @@ contract PolicyManager is Upgradeable {
* @notice Refund part of one node's fee by client
* @param _policyId Policy id
* @param _node Node address
**/
*/
function refund(bytes16 _policyId, address _node)
public returns (uint256 refundValue)
{
@ -415,7 +416,7 @@ contract PolicyManager is Upgradeable {
/**
* @notice Calculate amount of refund
* @param _policyId Policy id
**/
*/
function calculateRefundValue(bytes16 _policyId)
external view returns (uint256 refundValue)
{
@ -426,7 +427,7 @@ contract PolicyManager is Upgradeable {
* @notice Calculate amount of refund
* @param _policyId Policy id
* @param _node Node
**/
*/
function calculateRefundValue(bytes16 _policyId, address _node)
external view returns (uint256 refundValue)
{
@ -437,7 +438,7 @@ contract PolicyManager is Upgradeable {
/**
* @notice Get number of arrangements in the policy
* @param _policyId Policy id
**/
*/
function getArrangementsLength(bytes16 _policyId)
public view returns (uint256)
{
@ -448,7 +449,7 @@ contract PolicyManager is Upgradeable {
* @notice Get information about node reward
* @param _node Address of node
* @param _period Period to get reward delta
**/
*/
function getNodeRewardDelta(address _node, uint16 _period)
public view returns (int256)
{
@ -457,7 +458,7 @@ contract PolicyManager is Upgradeable {
/**
* @notice Return the information about arrangement
**/
*/
function getArrangementInfo(bytes16 _policyId, uint256 _index)
// TODO change to structure when ABIEncoderV2 is released (#1501)
// public view returns (ArrangementInfo)
@ -472,7 +473,7 @@ contract PolicyManager is Upgradeable {
/**
* @dev Get Policy structure by delegatecall
**/
*/
function delegateGetPolicy(address _target, bytes16 _policyId)
internal returns (Policy memory result)
{
@ -484,7 +485,7 @@ contract PolicyManager is Upgradeable {
/**
* @dev Get ArrangementInfo structure by delegatecall
**/
*/
function delegateGetArrangementInfo(address _target, bytes16 _policyId, uint256 _index)
internal returns (ArrangementInfo memory result)
{
@ -497,7 +498,7 @@ contract PolicyManager is Upgradeable {
/**
* @dev Get NodeInfo structure by delegatecall
**/
*/
function delegateGetNodeInfo(address _target, address _node)
internal returns (NodeInfo memory result)
{

View File

@ -7,7 +7,7 @@ import "zeppelin/ownership/Ownable.sol";
/**
* @notice Contract holds references to seed
* node interface information for bootstrapping the network.
**/
*/
contract Seeder is Ownable {
struct SeedInfo {
@ -21,14 +21,14 @@ contract Seeder is Ownable {
/**
* @param _maxSeeds The quantity of maximum seed nodes the contract can store
**/
*/
constructor(uint256 _maxSeeds) public {
seedArray = new address[](_maxSeeds);
}
/**
* @notice Returns the length of the seed nodes array
**/
*/
function getSeedArrayLength()
public view returns (uint256)
{
@ -39,7 +39,7 @@ contract Seeder is Ownable {
* @notice Write a new seed address and interface info to contract storage
* @param _ip IPv4 address of the seed node
* @param _port TCP port of the seed node
**/
*/
function enroll(address _seed, string memory _ip, uint16 _port) public onlyOwner {
seeds[_seed] = SeedInfo(_ip, _port);
@ -61,7 +61,7 @@ contract Seeder is Ownable {
* @notice Seed updates itself.
* @param _ip Updated IPv4 address of the existing seed node
* @param _port Updated TCP port of the existing seed node
**/
*/
function refresh(string memory _ip, uint16 _port) public {
SeedInfo storage seed = seeds[msg.sender];
require(seed.port != 0);

View File

@ -7,7 +7,7 @@ import "contracts/Issuer.sol";
/**
* @notice PolicyManager interface
**/
*/
contract PolicyManagerInterface {
function register(address _node, uint16 _period) external;
function updateReward(address _node, uint16 _period) external;
@ -17,7 +17,7 @@ contract PolicyManagerInterface {
/**
* @notice Adjudicator interface
**/
*/
contract AdjudicatorInterface {
function escrow() public view returns (address);
}
@ -25,7 +25,7 @@ contract AdjudicatorInterface {
/**
* @notice WorkLock interface
**/
*/
contract WorkLockInterface {
function escrow() public view returns (address);
}
@ -34,7 +34,8 @@ contract WorkLockInterface {
/**
* @notice Contract holds and locks stakers tokens.
* Each staker that locks their tokens will receive some compensation
**/
* @dev |v1.4.1|
*/
contract StakingEscrow is Issuer {
using SafeERC20 for NuCypherToken;
using AdditionalMath for uint256;
@ -129,7 +130,7 @@ contract StakingEscrow is Issuer {
* @param _minAllowableLockedTokens Min amount of tokens that can be locked
* @param _maxAllowableLockedTokens Max amount of tokens that can be locked
* @param _minWorkerPeriods Min amount of periods while a worker can't be changed
**/
*/
constructor(
NuCypherToken _token,
uint32 _hoursPerPeriod,
@ -160,7 +161,7 @@ contract StakingEscrow is Issuer {
/**
* @dev Checks the existence of a staker in the contract
**/
*/
modifier onlyStaker()
{
require(stakerInfo[msg.sender].value > 0);
@ -170,7 +171,7 @@ contract StakingEscrow is Issuer {
//------------------------Initialization------------------------
/**
* @notice Set policy manager address
**/
*/
function setPolicyManager(PolicyManagerInterface _policyManager) external onlyOwner {
// Policy manager can be set only once
require(address(policyManager) == address(0));
@ -181,7 +182,7 @@ contract StakingEscrow is Issuer {
/**
* @notice Set adjudicator address
**/
*/
function setAdjudicator(AdjudicatorInterface _adjudicator) external onlyOwner {
// Adjudicator can be set only once
require(address(adjudicator) == address(0));
@ -192,7 +193,7 @@ contract StakingEscrow is Issuer {
/**
* @notice Set worklock address
**/
*/
function setWorkLock(WorkLockInterface _workLock) external onlyOwner {
// WorkLock can be set only once
require(address(workLock) == address(0));
@ -204,7 +205,7 @@ contract StakingEscrow is Issuer {
//------------------------Main getters------------------------
/**
* @notice Get all tokens belonging to the staker
**/
*/
function getAllTokens(address _staker) external view returns (uint256) {
return stakerInfo[_staker].value;
}
@ -213,7 +214,7 @@ contract StakingEscrow is Issuer {
* @notice Get the start period. Use in the calculation of the last period of the sub stake
* @param _info Staker structure
* @param _currentPeriod Current period
**/
*/
function getStartPeriod(StakerInfo storage _info, uint16 _currentPeriod)
internal view returns (uint16)
{
@ -228,7 +229,7 @@ contract StakingEscrow is Issuer {
* @notice Get the last period of the sub stake
* @param _subStake Sub stake structure
* @param _startPeriod Pre-calculated start period
**/
*/
function getLastPeriodOfSubStake(SubStakeInfo storage _subStake, uint16 _startPeriod)
internal view returns (uint16)
{
@ -246,7 +247,7 @@ contract StakingEscrow is Issuer {
* @notice Get the last period of the sub stake
* @param _staker Staker
* @param _index Stake index
**/
*/
function getLastPeriodOfSubStake(address _staker, uint256 _index)
external view returns (uint16)
{
@ -263,7 +264,7 @@ contract StakingEscrow is Issuer {
* @param _info Staker structure
* @param _currentPeriod Current period
* @param _period Next period
**/
*/
function getLockedTokens(StakerInfo storage _info, uint16 _currentPeriod, uint16 _period)
internal view returns (uint256 lockedValue)
{
@ -282,7 +283,7 @@ contract StakingEscrow is Issuer {
* @dev This function is used by PreallocationEscrow so its signature can't be updated.
* @param _staker Staker
* @param _periods Amount of periods that will be added to the current period
**/
*/
function getLockedTokens(address _staker, uint16 _periods)
external view returns (uint256 lockedValue)
{
@ -297,7 +298,7 @@ contract StakingEscrow is Issuer {
* @dev Information may be incorrect for mined or unconfirmed surpassed period
* @param _staker Staker
* @param _periods Amount of periods that will be subtracted from the current period
**/
*/
function getLockedTokensInPast(address _staker, uint16 _periods)
external view returns (uint256 lockedValue)
{
@ -310,7 +311,7 @@ contract StakingEscrow is Issuer {
/**
* @notice Get the last active staker's period
* @param _staker Staker
**/
*/
function getLastActivePeriod(address _staker) public view returns (uint16) {
StakerInfo storage info = stakerInfo[_staker];
if (info.confirmedPeriod1 != EMPTY_CONFIRMED_PERIOD ||
@ -328,7 +329,7 @@ contract StakingEscrow is Issuer {
* @param _maxStakers Max stakers for looking, if set 0 then all will be used
* @return allLockedTokens Sum of locked tokens for active stakers
* @return activeStakers Array of stakers and their locked tokens. Stakers addresses stored as uint256
**/
*/
function getActiveStakers(uint16 _periods, uint256 _startIndex, uint256 _maxStakers)
external view returns (uint256 allLockedTokens, uint256[2][] memory activeStakers)
{
@ -367,14 +368,14 @@ contract StakingEscrow is Issuer {
/**
* @notice Checks if `reStake` parameter is available for changing
* @param _staker Staker
**/
*/
function isReStakeLocked(address _staker) public view returns (bool) {
return getCurrentPeriod() < stakerInfo[_staker].lockReStakeUntilPeriod;
}
/**
* @notice Get worker using staker's address
**/
*/
function getWorkerFromStaker(address _staker) public view returns (address) {
StakerInfo storage info = stakerInfo[_staker];
// specified address is not a staker
@ -386,14 +387,14 @@ contract StakingEscrow is Issuer {
/**
* @notice Get staker using worker's address
**/
*/
function getStakerFromWorker(address _worker) public view returns (address) {
return workerToStaker[_worker];
}
/**
* @notice Get work that completed by the staker
**/
*/
function getCompletedWork(address _staker) public view returns (uint256) {
return stakerInfo[_staker].completedWork;
}
@ -403,7 +404,7 @@ contract StakingEscrow is Issuer {
* @dev If specified period is outside all downtime periods, the length of the array will be returned
* @param _staker Staker
* @param _period Specified period number
**/
*/
function findIndexOfPastDowntime(address _staker, uint16 _period) external view returns (uint256 index) {
StakerInfo storage info = stakerInfo[_staker];
for (index = 0; index < info.pastDowntime.length; index++) {
@ -419,7 +420,7 @@ contract StakingEscrow is Issuer {
* @param _staker Staker
* @param _measureWork Value for `measureWork` parameter
* @return Work that was previously done
**/
*/
function setWorkMeasurement(address _staker, bool _measureWork) public returns (uint256) {
require(msg.sender == address(workLock));
StakerInfo storage info = stakerInfo[_staker];
@ -430,7 +431,7 @@ contract StakingEscrow is Issuer {
/** @notice Set worker
* @param _worker Worker address. Must be a real address, not a contract
**/
*/
function setWorker(address _worker) public onlyStaker {
StakerInfo storage info = stakerInfo[msg.sender];
require(_worker != info.worker, "Specified worker is already set for this staker");
@ -461,7 +462,7 @@ contract StakingEscrow is Issuer {
* @notice Set `reStake` parameter. If true then all staking rewards will be added to locked stake
* Only if this parameter is not locked
* @param _reStake Value for parameter
**/
*/
function setReStake(bool _reStake) public isInitialized {
require(!isReStakeLocked(msg.sender));
StakerInfo storage info = stakerInfo[msg.sender];
@ -475,7 +476,7 @@ contract StakingEscrow is Issuer {
/**
* @notice Lock `reStake` parameter. Only if this parameter is not locked
* @param _lockReStakeUntilPeriod Can't change `reStake` value until this period
**/
*/
function lockReStake(uint16 _lockReStakeUntilPeriod) public isInitialized {
require(!isReStakeLocked(msg.sender) &&
_lockReStakeUntilPeriod > getCurrentPeriod());
@ -490,7 +491,7 @@ contract StakingEscrow is Issuer {
* @param _value Amount of tokens to deposit
* @param _tokenContract Token contract address
* @notice (param _extraData) Amount of periods during which tokens will be locked
**/
*/
function receiveApproval(
address _from,
uint256 _value,
@ -517,7 +518,7 @@ contract StakingEscrow is Issuer {
* @notice Deposit tokens
* @param _value Amount of tokens to deposit
* @param _periods Amount of periods during which tokens will be locked
**/
*/
function deposit(uint256 _value, uint16 _periods) external {
deposit(msg.sender, msg.sender, _value, _periods);
}
@ -527,7 +528,7 @@ contract StakingEscrow is Issuer {
* @param _staker Staker
* @param _value Amount of tokens to deposit
* @param _periods Amount of periods during which tokens will be locked
**/
*/
function deposit(address _staker, uint256 _value, uint16 _periods) external {
deposit(_staker, msg.sender, _value, _periods);
}
@ -538,7 +539,7 @@ contract StakingEscrow is Issuer {
* @param _payer Owner of tokens
* @param _value Amount of tokens to deposit
* @param _periods Amount of periods during which tokens will be locked
**/
*/
function deposit(address _staker, address _payer, uint256 _value, uint16 _periods) internal isInitialized {
require(_value != 0);
StakerInfo storage info = stakerInfo[_staker];
@ -559,7 +560,7 @@ contract StakingEscrow is Issuer {
* @notice Lock some tokens as a stake
* @param _value Amount of tokens which will be locked
* @param _periods Amount of periods during which tokens will be locked
**/
*/
function lock(uint256 _value, uint16 _periods) external onlyStaker {
lock(msg.sender, _value, _periods);
}
@ -569,7 +570,7 @@ contract StakingEscrow is Issuer {
* @param _staker Staker
* @param _value Amount of tokens which will be locked
* @param _periods Amount of periods during which tokens will be locked
**/
*/
function lock(address _staker, uint256 _value, uint16 _periods) internal {
require(_value >= minAllowableLockedTokens &&
_periods >= minLockedPeriods);
@ -601,7 +602,7 @@ contract StakingEscrow is Issuer {
* @param _lastPeriod Last period of the sub stake
* @param _periods Duration of the sub stake in periods
* @param _lockedValue Amount of locked tokens
**/
*/
function saveSubStake(
StakerInfo storage _info,
uint16 _firstPeriod,
@ -635,7 +636,7 @@ contract StakingEscrow is Issuer {
* @param _index Index of the sub stake
* @param _newValue New sub stake value
* @param _periods Amount of periods for extending sub stake
**/
*/
function divideStake(uint256 _index, uint256 _newValue, uint16 _periods) external onlyStaker {
StakerInfo storage info = stakerInfo[msg.sender];
require(_newValue >= minAllowableLockedTokens && _periods > 0);
@ -658,7 +659,7 @@ contract StakingEscrow is Issuer {
* @notice Prolong active sub stake
* @param _index Index of the sub stake
* @param _periods Amount of periods for extending sub stake
**/
*/
function prolongStake(uint256 _index, uint16 _periods) external onlyStaker {
StakerInfo storage info = stakerInfo[msg.sender];
require(_periods > 0, "Incorrect parameters");
@ -681,7 +682,7 @@ contract StakingEscrow is Issuer {
/**
* @notice Withdraw available amount of tokens to staker
* @param _value Amount of tokens to withdraw
**/
*/
function withdraw(uint256 _value) external onlyStaker {
uint16 currentPeriod = getCurrentPeriod();
uint16 nextPeriod = currentPeriod + 1;
@ -698,7 +699,7 @@ contract StakingEscrow is Issuer {
/**
* @notice Confirm activity for the next period and mine for the previous period
**/
*/
function confirmActivity() external {
address staker = getStakerFromWorker(msg.sender);
StakerInfo storage info = stakerInfo[staker];
@ -745,7 +746,7 @@ contract StakingEscrow is Issuer {
/**
* @notice Mint tokens for previous periods if staker locked their tokens and confirmed activity
**/
*/
function mint() external onlyStaker {
// save last active period to the storage if both periods will be empty after minting
// because we won't be able to calculate last active period
@ -765,7 +766,7 @@ contract StakingEscrow is Issuer {
/**
* @notice Mint tokens for previous periods if staker locked their tokens and confirmed activity
* @param _staker Staker
**/
*/
function mint(address _staker) internal {
uint16 currentPeriod = getCurrentPeriod();
uint16 previousPeriod = currentPeriod - 1;
@ -813,7 +814,7 @@ contract StakingEscrow is Issuer {
* @param _confirmedPeriodNumber Number of confirmed period (1 or 2)
* @param _currentPeriod Current period
* @param _startPeriod Pre-calculated start period
**/
*/
function mint(
address _staker,
StakerInfo storage _info,
@ -864,7 +865,7 @@ contract StakingEscrow is Issuer {
* @param _penalty Penalty
* @param _investigator Investigator
* @param _reward Reward for the investigator
**/
*/
function slashStaker(
address _staker,
uint256 _penalty,
@ -927,7 +928,7 @@ contract StakingEscrow is Issuer {
* @return nextLock Amount of tokens that locked in the next period and not locked in the current period
* @return currentAndNextLock Amount of tokens that locked in the current period and in the next period
* @return shortestSubStakeIndex Index of the shortest sub stake
**/
*/
function getLockedTokensAndShortestSubStake(
StakerInfo storage _info,
uint16 _currentPeriod,
@ -980,7 +981,7 @@ contract StakingEscrow is Issuer {
* @param _decreasePeriod The period when the decrease begins
* @param _startPeriod Pre-calculated start period
* @param _shortestSubStakeIndex Index of the shortest period
**/
*/
function decreaseSubStakes(
StakerInfo storage _info,
uint256 _penalty,
@ -1037,7 +1038,7 @@ contract StakingEscrow is Issuer {
* @return shortestSubStake The shortest sub stake
* @return minSubStakeDuration Duration of the shortest sub stake
* @return minSubStakeLastPeriod Last period of the shortest sub stake
**/
*/
function getShortestSubStake(
StakerInfo storage _info,
uint16 _currentPeriod,
@ -1078,7 +1079,7 @@ contract StakingEscrow is Issuer {
* @param _firstPeriod First period of the old sub stake
* @param _lockedValue Locked value of the old sub stake
* @param _currentPeriod Current period, when the old sub stake is already unlocked
**/
*/
function saveOldSubStake(
StakerInfo storage _info,
uint16 _firstPeriod,
@ -1117,21 +1118,21 @@ contract StakingEscrow is Issuer {
//-------------Additional getters for stakers info-------------
/**
* @notice Return the length of the array of stakers
**/
*/
function getStakersLength() external view returns (uint256) {
return stakers.length;
}
/**
* @notice Return the length of the array of sub stakes
**/
*/
function getSubStakesLength(address _staker) external view returns (uint256) {
return stakerInfo[_staker].subStakes.length;
}
/**
* @notice Return the information about sub stake
**/
*/
function getSubStakeInfo(address _staker, uint256 _index)
// TODO change to structure when ABIEncoderV2 is released (#1501)
// public view returns (SubStakeInfo)
@ -1146,14 +1147,14 @@ contract StakingEscrow is Issuer {
/**
* @notice Return the length of the array of past downtime
**/
*/
function getPastDowntimeLength(address _staker) external view returns (uint256) {
return stakerInfo[_staker].pastDowntime.length;
}
/**
* @notice Return the information about past downtime
**/
*/
function getPastDowntime(address _staker, uint256 _index)
// TODO change to structure when ABIEncoderV2 is released (#1501)
// public view returns (Downtime)
@ -1168,7 +1169,7 @@ contract StakingEscrow is Issuer {
//------------------------Upgradeable------------------------
/**
* @dev Get StakerInfo structure by delegatecall
**/
*/
function delegateGetStakerInfo(address _target, bytes32 _staker)
internal returns (StakerInfo memory result)
{
@ -1185,7 +1186,7 @@ contract StakingEscrow is Issuer {
/**
* @dev Get SubStakeInfo structure by delegatecall
**/
*/
function delegateGetSubStakeInfo(address _target, bytes32 _staker, uint256 _index)
internal returns (SubStakeInfo memory result)
{
@ -1198,7 +1199,7 @@ contract StakingEscrow is Issuer {
/**
* @dev Get Downtime structure by delegatecall
**/
*/
function delegateGetPastDowntime(address _target, bytes32 _staker, uint256 _index)
internal returns (Downtime memory result)
{

View File

@ -9,7 +9,7 @@ import "contracts/StakingEscrow.sol";
/**
* @notice The WorkLock distribution contract
**/
*/
contract WorkLock {
using SafeMath for uint256;
using Address for address payable;
@ -46,7 +46,7 @@ contract WorkLock {
* @param _depositRate ETH -> NU rate
* @param _refundRate Work -> ETH rate
* @param _lockedPeriods Number of periods during which claimed tokens will be locked
**/
*/
constructor(
NuCypherToken _token,
StakingEscrow _escrow,
@ -78,7 +78,7 @@ contract WorkLock {
/**
* @notice Bid for tokens by transferring ETH
**/
*/
function bid() public payable returns (uint256 newClaimedTokens) {
require(block.timestamp >= startBidDate && block.timestamp <= endBidDate,
"Bid is open during a certain period");
@ -96,7 +96,7 @@ contract WorkLock {
/**
* @notice Claimed tokens will be deposited and locked as stake in the StakingEscrow contract.
**/
*/
function claim() public returns (uint256 claimedTokens) {
require(block.timestamp >= endBidDate, "Claiming tokens allowed after bidding is over");
WorkInfo storage info = workInfo[msg.sender];
@ -111,7 +111,7 @@ contract WorkLock {
/**
* @notice Refund ETH for the completed work
**/
*/
function refund() public returns (uint256 refundETH) {
WorkInfo storage info = workInfo[msg.sender];
require(info.claimed, "Tokens are not claimed");
@ -135,7 +135,7 @@ contract WorkLock {
/**
* @notice Get remaining work to full refund
**/
*/
function getRemainingWork(address _staker) public view returns (uint256) {
WorkInfo storage info = workInfo[_staker];
uint256 completedWork = escrow.getCompletedWork(_staker).sub(info.completedWork);

View File

@ -6,7 +6,7 @@ import "zeppelin/math/SafeMath.sol";
/**
* @notice Additional math operations
**/
*/
library AdditionalMath {
using SafeMath for uint256;
@ -20,7 +20,7 @@ library AdditionalMath {
/**
* @notice Division and ceil
**/
*/
function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
return (a.add(b) - 1) / b;
}

View File

@ -5,7 +5,7 @@ import "contracts/lib/SignatureVerifier.sol";
/**
* @notice Validates re-encryption correctness.
**/
*/
library ReEncryptionValidator {
using UmbralDeserializer for bytes;
@ -43,7 +43,7 @@ library ReEncryptionValidator {
* @param _capsuleBytes Capsule
* @param _cFragBytes Capsule frag
* @param _precomputedBytes Additional precomputed data
**/
*/
function validateCFrag(
bytes memory _capsuleBytes,
bytes memory _cFragBytes,

View File

@ -4,7 +4,7 @@ pragma solidity ^0.5.3;
/**
* @notice Library to recover address and verify signatures
* @dev Simple wrapper for `ecrecover`
**/
*/
library SignatureVerifier {
enum HashAlgorithm {KECCAK256, SHA256, RIPEMD160}
@ -16,7 +16,7 @@ library SignatureVerifier {
* @notice Recover signer address from hash and signature
* @param _hash 32 bytes message hash
* @param _signature Signature of hash - 32 bytes r + 32 bytes s + 1 byte v (could be 0, 1, 27, 28)
**/
*/
function recover(bytes32 _hash, bytes memory _signature)
internal
pure
@ -44,7 +44,7 @@ library SignatureVerifier {
/**
* @notice Transform public key to address
* @param _publicKey secp256k1 public key
**/
*/
function toAddress(bytes memory _publicKey) internal pure returns (address) {
return address(uint160(uint256(keccak256(_publicKey))));
}
@ -53,7 +53,7 @@ library SignatureVerifier {
* @notice Hash using one of pre built hashing algorithm
* @param _message Signed message
* @param _algorithm Hashing algorithm
**/
*/
function hash(bytes memory _message, HashAlgorithm _algorithm)
internal
pure
@ -75,7 +75,7 @@ library SignatureVerifier {
* @param _signature Signature of message hash
* @param _publicKey secp256k1 public key in uncompressed format without prefix byte (64 bytes)
* @param _algorithm Hashing algorithm
**/
*/
function verify(
bytes memory _message,
bytes memory _signature,
@ -96,7 +96,7 @@ library SignatureVerifier {
* @dev Only supports version 0 and version E (0x45)
* @param _message Message to sign
* @param _version EIP191 version to use
**/
*/
function hashEIP191(
bytes memory _message,
byte _version
@ -140,7 +140,7 @@ library SignatureVerifier {
* @param _signature Signature of message hash
* @param _publicKey secp256k1 public key in uncompressed format without prefix byte (64 bytes)
* @param _version EIP191 version to use
**/
*/
function verifyEIP191(
bytes memory _message,
bytes memory _signature,

View File

@ -3,7 +3,7 @@ pragma solidity ^0.5.3;
/**
* @notice Deserialization library for Umbral objects
**/
*/
library UmbralDeserializer {
struct Point {
@ -72,7 +72,7 @@ library UmbralDeserializer {
/**
* @notice Deserialize to capsule (not activated)
**/
*/
function toCapsule(bytes memory _capsuleBytes)
internal pure returns (Capsule memory capsule)
{
@ -87,7 +87,7 @@ library UmbralDeserializer {
* @notice Deserialize to correctness proof
* @param _pointer Proof bytes memory pointer
* @param _proofBytesLength Proof bytes length
**/
*/
function toCorrectnessProof(uint256 _pointer, uint256 _proofBytesLength)
internal pure returns (CorrectnessProof memory proof)
{
@ -111,7 +111,7 @@ library UmbralDeserializer {
/**
* @notice Deserialize to correctness proof
**/
*/
function toCorrectnessProof(bytes memory _proofBytes)
internal pure returns (CorrectnessProof memory proof)
{
@ -121,7 +121,7 @@ library UmbralDeserializer {
/**
* @notice Deserialize to CapsuleFrag
**/
*/
function toCapsuleFrag(bytes memory _cFragBytes)
internal pure returns (CapsuleFrag memory cFrag)
{
@ -140,7 +140,7 @@ library UmbralDeserializer {
/**
* @notice Deserialize to precomputed data
**/
*/
function toPreComputedData(bytes memory _preComputedData)
internal pure returns (PreComputedData memory data)
{
@ -229,7 +229,7 @@ library UmbralDeserializer {
// TODO extract to external library if needed (#1500)
/**
* @notice Get the memory pointer for start of array
**/
*/
function getPointer(bytes memory _bytes) internal pure returns (uint256 pointer) {
assembly {
pointer := add(_bytes, 32) // skip array length
@ -238,7 +238,7 @@ library UmbralDeserializer {
/**
* @notice Copy point data from memory in the pointer position
**/
*/
function copyPoint(uint256 _pointer, Point memory _point)
internal pure returns (uint256 resultPointer)
{
@ -256,7 +256,7 @@ library UmbralDeserializer {
/**
* @notice Read 1 byte from memory in the pointer position
**/
*/
function getByte(uint256 _pointer) internal pure returns (byte result) {
bytes32 word;
assembly {
@ -268,7 +268,7 @@ library UmbralDeserializer {
/**
* @notice Read 32 bytes from memory in the pointer position
**/
*/
function getBytes32(uint256 _pointer) internal pure returns (bytes32 result) {
assembly {
result := mload(_pointer)
@ -282,7 +282,7 @@ library UmbralDeserializer {
* @param _bytesPointer Source memory pointer
* @param _target Target array
* @param _bytesLength Number of bytes to copy
**/
*/
function copyBytes(uint256 _bytesPointer, bytes memory _target, uint256 _bytesLength)
internal
pure

View File

@ -8,7 +8,7 @@ import "zeppelin/utils/Address.sol";
/**
* @notice Proxying requests to other contracts.
* Client should use ABI of real contract and address of this contract
**/
*/
contract Dispatcher is Upgradeable {
using Address for address;
@ -17,7 +17,7 @@ contract Dispatcher is Upgradeable {
/**
* @dev Set upgrading status before and after operations
**/
*/
modifier upgrading()
{
isUpgrade = UPGRADE_TRUE;
@ -28,7 +28,7 @@ contract Dispatcher is Upgradeable {
/**
* @param _target Target contract address
* @param _newSecretHash Secret hash (keccak256)
**/
*/
constructor(address _target, bytes32 _newSecretHash) public upgrading {
require(_target.isContract());
// Checks that target contract inherits Dispatcher state
@ -46,7 +46,7 @@ contract Dispatcher is Upgradeable {
* @param _target New target contract address
* @param _secret Secret for proof of contract owning
* @param _newSecretHash New secret hash (keccak256)
**/
*/
function upgrade(address _target, bytes memory _secret, bytes32 _newSecretHash) public onlyOwner upgrading {
require(_target.isContract());
require(keccak256(_secret) == secretHash && _newSecretHash != secretHash);
@ -69,7 +69,7 @@ contract Dispatcher is Upgradeable {
* @dev Test storage carefully before upgrade again after rollback
* @param _secret Secret for proof of contract owning
* @param _newSecretHash New secret hash (keccak256)
**/
*/
function rollback(bytes memory _secret, bytes32 _newSecretHash) public onlyOwner upgrading {
require(previousTarget.isContract());
require(keccak256(_secret) == secretHash && _newSecretHash != secretHash);
@ -88,7 +88,7 @@ contract Dispatcher is Upgradeable {
/**
* @dev Call verifyState method for Upgradeable contract
**/
*/
function verifyUpgradeableState(address _from, address _to) private {
(bool callSuccess,) = _from.delegatecall(abi.encodeWithSignature("verifyState(address)", _to));
require(callSuccess);
@ -96,7 +96,7 @@ contract Dispatcher is Upgradeable {
/**
* @dev Call finishUpgrade method from the Upgradeable contract
**/
*/
function finishUpgrade() private {
(bool callSuccess,) = target.delegatecall(abi.encodeWithSignature("finishUpgrade(address)", target));
require(callSuccess);
@ -113,12 +113,12 @@ contract Dispatcher is Upgradeable {
/**
* @dev Override function using empty code because no reason to call this function in Dispatcher
**/
*/
function finishUpgrade(address) public {}
/**
* @dev Fallback function send all requests to the target contract
**/
*/
function () external payable {
assert(target.isContract());
// execute requested function from target contract using storage of the dispatcher

View File

@ -9,7 +9,7 @@ import "zeppelin/ownership/Ownable.sol";
* @dev Inherited contract should implement verifyState(address) method by checking storage variables
* (see verifyState(address) in Dispatcher). Also contract should implement finishUpgrade(address)
* if it is using constructor parameters by coping this parameters to the dispatcher storage
**/
*/
contract Upgradeable is Ownable {
event StateVerified(address indexed testTarget, address sender);
@ -19,22 +19,22 @@ contract Upgradeable is Ownable {
* @dev Contracts at the target must reserve the same location in storage for this address as in Dispatcher
* Stored data actually lives in the Dispatcher
* However the storage layout is specified here in the implementing contracts
**/
*/
address public target;
/**
* @dev Previous contract address (if available). Used for rollback
**/
*/
address public previousTarget;
/**
* @dev Secret hash to proof that user owns previous version of a contract
**/
*/
bytes32 public secretHash;
/**
* @dev Upgrade status. Explicit `uint8` type is used instead of `bool` to save gas by excluding 0 value
**/
*/
uint8 public isUpgrade;
/** Constants for `isUpgrade` field **/
@ -44,7 +44,7 @@ contract Upgradeable is Ownable {
/**
* @dev Checks that function executed while upgrading
* Recommended to add to `verifyState` and `finishUpgrade` methods
**/
*/
modifier onlyWhileUpgrading()
{
require(isUpgrade == UPGRADE_TRUE);
@ -54,7 +54,7 @@ contract Upgradeable is Ownable {
/**
* @dev Method for verifying storage state.
* Should check that new target contract returns right storage value
**/
*/
function verifyState(address _testTarget) public onlyWhileUpgrading {
emit StateVerified(_testTarget, msg.sender);
}
@ -62,7 +62,7 @@ contract Upgradeable is Ownable {
/**
* @dev Copy values from the new target to the current storage
* @param _target New target contract address
**/
*/
function finishUpgrade(address _target) public onlyWhileUpgrading {
emit UpgradeFinished(_target, msg.sender);
}
@ -75,7 +75,7 @@ contract Upgradeable is Ownable {
* @param _argument1 First method argument
* @param _argument2 Second method argument
* @return Address in memory where the data is located
**/
*/
function delegateGetData(
address _target,
string memory _signature,
@ -108,7 +108,7 @@ contract Upgradeable is Ownable {
/**
* @dev Call "getter" without parameters.
* Result should not exceed 32 bytes
**/
*/
function delegateGet(address _target, string memory _signature)
internal returns (uint256 result)
{
@ -121,7 +121,7 @@ contract Upgradeable is Ownable {
/**
* @dev Call "getter" with one parameter.
* Result should not exceed 32 bytes
**/
*/
function delegateGet(address _target, string memory _signature, bytes32 _argument)
internal returns (uint256 result)
{
@ -134,7 +134,7 @@ contract Upgradeable is Ownable {
/**
* @dev Call "getter" with two parameters.
* Result should not exceed 32 bytes
**/
*/
function delegateGet(
address _target,
string memory _signature,

View File

@ -7,7 +7,7 @@ import "zeppelin/utils/Address.sol";
/**
* @notice Router for accessing interface contract
**/
*/
contract StakingInterfaceRouter is Ownable {
using Address for address;
@ -17,7 +17,7 @@ contract StakingInterfaceRouter is Ownable {
/**
* @param _target Address of the interface contract
* @param _newSecretHash Secret hash (keccak256)
**/
*/
constructor(address _target, bytes32 _newSecretHash) public {
require(_target.isContract());
target = _target;
@ -29,7 +29,7 @@ contract StakingInterfaceRouter is Ownable {
* @param _target New contract address
* @param _secret Secret for proof of contract owning
* @param _newSecretHash New secret hash (keccak256)
**/
*/
function upgrade(address _target, bytes memory _secret, bytes32 _newSecretHash) public onlyOwner {
require(_target.isContract());
require(keccak256(_secret) == secretHash && _newSecretHash != secretHash);
@ -43,7 +43,7 @@ contract StakingInterfaceRouter is Ownable {
/**
* @notice Base class for any staking contract
* @dev Implement `isFallbackAllowed()` or override fallback function
**/
*/
contract AbstractStakingContract {
using Address for address;
@ -51,7 +51,7 @@ contract AbstractStakingContract {
/**
* @param _router Interface router contract address
**/
*/
constructor(StakingInterfaceRouter _router) public {
// check that the input address is contract
require(_router.target().isContract());
@ -60,12 +60,12 @@ contract AbstractStakingContract {
/**
* @dev Checks permission for calling fallback function
**/
*/
function isFallbackAllowed() public returns (bool);
/**
* @dev Function sends all requests to the target contract
**/
*/
function () external payable {
require(isFallbackAllowed());
address target = router.target();

View File

@ -10,7 +10,7 @@ import "contracts/staking_contracts/AbstractStakingContract.sol";
/**
* @notice StakingEscrow interface
**/
*/
contract StakingEscrowInterface {
function getAllTokens(address _staker) public view returns (uint256);
function secondsPerPeriod() public view returns (uint32);
@ -19,7 +19,7 @@ contract StakingEscrowInterface {
/**
* @notice Contract holds tokens for vesting.
* Also tokens can be used as a stake in the staking escrow contract
**/
*/
contract PreallocationEscrow is AbstractStakingContract, Ownable {
using SafeERC20 for NuCypherToken;
using SafeMath for uint256;
@ -38,7 +38,7 @@ contract PreallocationEscrow is AbstractStakingContract, Ownable {
* @param _router Address of the StakingInterfaceRouter contract
* @param _token Address of the NuCypher token contract
* @param _stakingEscrow Address of the StakingEscrow contract
**/
*/
constructor(
StakingInterfaceRouter _router,
NuCypherToken _token,
@ -56,7 +56,7 @@ contract PreallocationEscrow is AbstractStakingContract, Ownable {
* @notice Initial tokens deposit
* @param _value Amount of token to deposit
* @param _duration Duration of tokens locking
**/
*/
function initialDeposit(uint256 _value, uint256 _duration) public {
require(lockedValue == 0 && _value > 0);
endLockTimestamp = block.timestamp.add(_duration);
@ -67,7 +67,7 @@ contract PreallocationEscrow is AbstractStakingContract, Ownable {
/**
* @notice Get locked tokens value
**/
*/
function getLockedTokens() public view returns (uint256) {
if (endLockTimestamp <= block.timestamp) {
return 0;
@ -78,7 +78,7 @@ contract PreallocationEscrow is AbstractStakingContract, Ownable {
/**
* @notice Withdraw available amount of tokens to owner
* @param _value Amount of token to withdraw
**/
*/
function withdrawTokens(uint256 _value) public onlyOwner {
uint256 balance = token.balanceOf(address(this));
require(balance >= _value);
@ -91,7 +91,7 @@ contract PreallocationEscrow is AbstractStakingContract, Ownable {
/**
* @notice Withdraw available ETH to the owner
**/
*/
function withdrawETH() public onlyOwner {
uint256 balance = address(this).balance;
require(balance != 0);
@ -101,7 +101,7 @@ contract PreallocationEscrow is AbstractStakingContract, Ownable {
/**
* @notice Calling fallback function is allowed only for the owner
**/
*/
function isFallbackAllowed() public returns (bool) {
return msg.sender == owner();
}

View File

@ -9,9 +9,10 @@ import "contracts/PolicyManager.sol";
/**
* @notice Interface for accessing main contracts from a staking contract
* @dev All methods must be stateless because this code will execute by delegatecall call
* If state is needed - use getStateContract() method to access state of this contract
**/
* @dev All methods must be stateless because this code will be executed by delegatecall call.
* If state is needed - use getStateContract() method to access state of this contract.
* @dev |v1.2.1|
*/
contract StakingInterface {
event DepositedAsStaker(address indexed sender, uint256 value, uint16 periods);
@ -35,7 +36,7 @@ contract StakingInterface {
* @param _token Token contract
* @param _escrow Escrow contract
* @param _policyManager PolicyManager contract
**/
*/
constructor(
NuCypherToken _token,
StakingEscrow _escrow,
@ -54,7 +55,7 @@ contract StakingInterface {
/**
* @notice Get contract which stores state
* @dev Assume that `this` is the staking contract
**/
*/
function getStateContract() internal view returns (StakingInterface) {
address payable stakingContractAddress = address(bytes20(address(this)));
StakingInterfaceRouter router = AbstractStakingContract(stakingContractAddress).router();
@ -64,7 +65,7 @@ contract StakingInterface {
/**
* @notice Set `worker` parameter in the staking escrow
* @param _worker Worker address
**/
*/
function setWorker(address _worker) public {
getStateContract().escrow().setWorker(_worker);
emit WorkerSet(msg.sender, _worker);
@ -73,7 +74,7 @@ contract StakingInterface {
/**
* @notice Set `reStake` parameter in the staking escrow
* @param _reStake Value for parameter
**/
*/
function setReStake(bool _reStake) public {
getStateContract().escrow().setReStake(_reStake);
emit ReStakeSet(msg.sender, _reStake);
@ -82,7 +83,7 @@ contract StakingInterface {
/**
* @notice Lock `reStake` parameter in the staking escrow
* @param _lockReStakeUntilPeriod Can't change `reStake` value until this period
**/
*/
function lockReStake(uint16 _lockReStakeUntilPeriod) public {
getStateContract().escrow().lockReStake(_lockReStakeUntilPeriod);
emit ReStakeLocked(msg.sender, _lockReStakeUntilPeriod);
@ -92,7 +93,7 @@ contract StakingInterface {
* @notice Deposit tokens to the staking escrow
* @param _value Amount of token to deposit
* @param _periods Amount of periods during which tokens will be locked
**/
*/
function depositAsStaker(uint256 _value, uint16 _periods) public {
StakingInterface state = getStateContract();
NuCypherToken tokenFromState = state.token();
@ -106,7 +107,7 @@ contract StakingInterface {
/**
* @notice Withdraw available amount of tokens from the staking escrow to the staking contract
* @param _value Amount of token to withdraw
**/
*/
function withdrawAsStaker(uint256 _value) public {
getStateContract().escrow().withdraw(_value);
emit WithdrawnAsStaker(msg.sender, _value);
@ -116,7 +117,7 @@ contract StakingInterface {
* @notice Lock some tokens or increase lock in the staking escrow
* @param _value Amount of tokens which should lock
* @param _periods Amount of periods during which tokens will be locked
**/
*/
function lock(uint256 _value, uint16 _periods) public {
getStateContract().escrow().lock(_value, _periods);
emit Locked(msg.sender, _value, _periods);
@ -127,7 +128,7 @@ contract StakingInterface {
* @param _index Index of stake
* @param _newValue New stake value
* @param _periods Amount of periods for extending stake
**/
*/
function divideStake(
uint256 _index,
uint256 _newValue,
@ -141,7 +142,7 @@ contract StakingInterface {
/**
* @notice Mint tokens in the staking escrow
**/
*/
function mint() external {
getStateContract().escrow().mint();
emit Mined(msg.sender);
@ -149,7 +150,7 @@ contract StakingInterface {
/**
* @notice Withdraw available reward from the policy manager to the staking contract
**/
*/
function withdrawPolicyReward(address payable _recipient) public {
uint256 value = getStateContract().policyManager().withdraw(_recipient);
emit PolicyRewardWithdrawn(_recipient, value);
@ -157,7 +158,7 @@ contract StakingInterface {
/**
* @notice Set the minimum reward that the staker will take in the policy manager
**/
*/
function setMinRewardRate(uint256 _minRewardRate) public {
getStateContract().policyManager().setMinRewardRate(_minRewardRate);
emit MinRewardRateSet(msg.sender, _minRewardRate);
@ -168,7 +169,7 @@ contract StakingInterface {
* @notice Prolong active sub stake
* @param _index Index of the sub stake
* @param _periods Amount of periods for extending sub stake
**/
*/
function prolongStake(uint256 _index, uint16 _periods) public {
getStateContract().escrow().prolongStake(_index, _periods);
emit Prolonged(msg.sender, _index, _periods);

View File

@ -135,12 +135,13 @@ def inspect(provider_uri, config_root, registry_infile, deployer_address, poa):
@_admin_actor_options
@click.option('--retarget', '-d', help="Retarget a contract's proxy.", is_flag=True)
@click.option('--target-address', help="Address of the target contract", type=EIP55_CHECKSUM_ADDRESS)
@click.option('--ignore-deployed', help="Ignore already deployed contracts if exist.", is_flag=True)
def upgrade(# Admin Actor Options
provider_uri, contract_name, config_root, poa, force, etherscan, hw_wallet, deployer_address,
registry_infile, registry_outfile, dev,
# Other
retarget, target_address):
retarget, target_address, ignore_deployed):
"""
Upgrade NuCypher existing proxy contract deployments.
"""
@ -188,7 +189,8 @@ def upgrade(# Admin Actor Options
click.confirm(f"Confirm deploy new version of {contract_name} and retarget proxy?", abort=True)
receipts = ADMINISTRATOR.upgrade_contract(contract_name=contract_name,
existing_plaintext_secret=existing_secret,
new_plaintext_secret=new_secret)
new_plaintext_secret=new_secret,
ignore_deployed=ignore_deployed)
emitter.message(f"Successfully deployed and upgraded {contract_name}", color='green')
for name, receipt in receipts.items():
paint_receipt_summary(emitter=emitter, receipt=receipt)
@ -237,12 +239,13 @@ def rollback(# Admin Actor Options
@_admin_actor_options
@click.option('--bare', help="Deploy a contract *only* without any additional operations.", is_flag=True)
@click.option('--gas', help="Operate with a specified gas per-transaction limit", type=click.IntRange(min=1))
@click.option('--ignore-deployed', help="Ignore already deployed contracts if exist.", is_flag=True)
def contracts(# Admin Actor Options
provider_uri, contract_name, config_root, poa, force, etherscan, hw_wallet, deployer_address,
registry_infile, registry_outfile, dev,
# Other
bare, gas):
bare, gas, ignore_deployed):
"""
Compile and deploy contracts.
"""
@ -288,12 +291,14 @@ def contracts(# Admin Actor Options
receipts, agent = ADMINISTRATOR.deploy_contract(contract_name=contract_name,
plaintext_secret=secret,
gas_limit=gas,
bare=bare)
bare=bare,
ignore_deployed=ignore_deployed)
else:
# Non-Upgradeable or Bare
receipts, agent = ADMINISTRATOR.deploy_contract(contract_name=contract_name,
gas_limit=gas,
bare=bare)
bare=bare,
ignore_deployed=ignore_deployed)
# Report
paint_contract_deployment(contract_name=contract_name,
@ -330,7 +335,8 @@ def contracts(# Admin Actor Options
deployment_receipts = ADMINISTRATOR.deploy_network_contracts(secrets=secrets,
emitter=emitter,
interactive=not force,
etherscan=etherscan)
etherscan=etherscan,
ignore_deployed=ignore_deployed)
# Paint outfile paths
registry_outfile = local_registry.filepath

View File

@ -260,7 +260,7 @@ Registry ................ {registry.filepath}
from nucypher.blockchain.eth.actors import ContractAdministrator
for contract_deployer_class in ContractAdministrator.dispatched_upgradeable_deployer_classes:
try:
bare_contract = blockchain.get_contract_by_name(name=contract_deployer_class.contract_name,
bare_contract = blockchain.get_contract_by_name(contract_name=contract_deployer_class.contract_name,
proxy_name=DispatcherDeployer.contract_name,
registry=registry,
use_proxy_address=False)
@ -297,7 +297,7 @@ Registry ................ {registry.filepath}
#
staking_interface_agent = PreallocationEscrowAgent.StakingInterfaceAgent(registry=registry)
bare_contract = blockchain.get_contract_by_name(name=staking_interface_agent.contract_name,
bare_contract = blockchain.get_contract_by_name(contract_name=staking_interface_agent.contract_name,
proxy_name=StakingInterfaceRouterDeployer.contract_name,
use_proxy_address=False,
registry=registry)

View File

@ -69,7 +69,7 @@ class TesterBlockchain(BlockchainDeployerInterface):
_PROVIDER_URI = 'tester://pyevm'
TEST_CONTRACTS_DIR = os.path.join(BASE_DIR, 'tests', 'blockchain', 'eth', 'contracts', 'contracts')
_compiler = SolidityCompiler(test_contract_dir=TEST_CONTRACTS_DIR)
_compiler = SolidityCompiler(source_dirs=[(SolidityCompiler.default_contract_dir(), {TEST_CONTRACTS_DIR})])
_test_account_cache = list()
_default_test_accounts = NUMBER_OF_ETH_TEST_ACCOUNTS

View File

@ -9,7 +9,7 @@ import "contracts/proxy/Upgradeable.sol";
/**
* @notice Contract for testing the Adjudicator contract
**/
*/
contract StakingEscrowForAdjudicatorMock {
uint32 public secondsPerPeriod = 1;
@ -50,7 +50,7 @@ contract StakingEscrowForAdjudicatorMock {
/**
* @notice Upgrade to this contract must lead to fail
**/
*/
contract AdjudicatorBad is Upgradeable {
StakingEscrow public escrow;
@ -67,7 +67,7 @@ contract AdjudicatorBad is Upgradeable {
/**
* @notice Contract for testing upgrading the Adjudicator contract
**/
*/
contract AdjudicatorV2Mock is Adjudicator {
uint256 public valueToCheck;

View File

@ -8,7 +8,7 @@ import "contracts/proxy/Upgradeable.sol";
/**
* @dev Contract for testing internal methods in the Issuer contract
**/
*/
contract IssuerMock is Issuer {
constructor(
@ -54,7 +54,7 @@ contract IssuerMock is Issuer {
/**
* @notice Upgrade to this contract must lead to fail
**/
*/
contract IssuerBad is Upgradeable {
address public token;
@ -72,7 +72,7 @@ contract IssuerBad is Upgradeable {
/**
* @notice Contract for testing upgrading the Issuer contract
**/
*/
contract IssuerV2Mock is Issuer {
uint256 public valueToCheck;

View File

@ -7,7 +7,7 @@ import "contracts/lib/ReEncryptionValidator.sol";
/**
* @notice Contract for using SignatureVerifier library
**/
*/
contract SignatureVerifierMock {
function recover(bytes32 _hash, bytes memory _signature)
@ -72,7 +72,7 @@ contract SignatureVerifierMock {
/**
* @dev Contract for testing UmbralDeserializer library
**/
*/
contract UmbralDeserializerMock {
using UmbralDeserializer for bytes;
@ -178,7 +178,7 @@ contract UmbralDeserializerMock {
/**
* @notice Contract for using ReEncryptionValidator library
**/
*/
contract ReEncryptionValidatorMock {
using UmbralDeserializer for bytes;

View File

@ -7,7 +7,7 @@ import "contracts/StakingEscrow.sol";
/**
* @notice Upgrade to this contract must lead to fail
**/
*/
contract PolicyManagerBad is PolicyManager {
constructor(StakingEscrow _escrow) public PolicyManager(_escrow) {
@ -22,7 +22,7 @@ contract PolicyManagerBad is PolicyManager {
/**
* @notice Contract for testing upgrading the PolicyManager contract
**/
*/
contract PolicyManagerV2Mock is PolicyManager {
uint256 public valueToCheck;
@ -43,7 +43,7 @@ contract PolicyManagerV2Mock is PolicyManager {
/**
* @notice Contract for using in PolicyManager tests
**/
*/
contract StakingEscrowForPolicyMock {
struct Downtime {
@ -58,28 +58,28 @@ contract StakingEscrowForPolicyMock {
/**
* @param _hoursPerPeriod Size of period in hours
**/
*/
constructor(uint16 _hoursPerPeriod) public {
secondsPerPeriod = uint32(_hoursPerPeriod * 1 hours);
}
/**
* @return Number of current period
**/
*/
function getCurrentPeriod() public view returns (uint16) {
return uint16(block.timestamp / secondsPerPeriod);
}
/**
* @notice Set last active period
**/
*/
function setLastActivePeriod(uint16 _lastActivePeriod) external {
lastActivePeriod = _lastActivePeriod;
}
/**
* @notice Add downtime period
**/
*/
function pushDowntimePeriod(uint16 _startPeriod, uint16 _endPeriod) external {
downtime.push(Downtime(_startPeriod, _endPeriod));
}
@ -89,7 +89,7 @@ contract StakingEscrowForPolicyMock {
* @param _staker Staker on behalf of whom minting will be
* @param _startPeriod Start period for minting
* @param _numberOfPeriods Number periods for minting
**/
*/
function mint(address _staker, uint16 _startPeriod, uint16 _numberOfPeriods) public {
for (uint16 i = 0; i < _numberOfPeriods; i++) {
policyManager.updateReward(_staker, i + _startPeriod);
@ -100,14 +100,14 @@ contract StakingEscrowForPolicyMock {
* @notice Emulate mint method
* @param _startPeriod Start period for minting
* @param _numberOfPeriods Number periods for minting
**/
*/
function mint(uint16 _startPeriod, uint16 _numberOfPeriods) external {
mint(msg.sender, _startPeriod, _numberOfPeriods);
}
/**
* @notice Set policy manager address
**/
*/
function setPolicyManager(PolicyManager _policyManager) external {
policyManager = _policyManager;
}

View File

@ -3,7 +3,7 @@ pragma solidity ^0.5.3;
/**
* @notice Contract for using in token tests
**/
*/
contract ReceiveApprovalMethodMock {
address public sender;

View File

@ -6,7 +6,7 @@ import "contracts/NuCypherToken.sol";
/**
* @notice Contract for using in staking contracts tests
**/
*/
contract StakingEscrowForStakingContractMock {
NuCypherToken token;
@ -83,7 +83,7 @@ contract StakingEscrowForStakingContractMock {
/**
* @notice Contract for staking contract tests
**/
*/
contract PolicyManagerForStakingContractMock {
uint32 public secondsPerPeriod = 1;
@ -110,7 +110,7 @@ contract PolicyManagerForStakingContractMock {
/**
* @notice Contract for staking contract tests
**/
*/
contract StakingInterfaceMockV1 {
function firstMethod() public pure {}
@ -124,7 +124,7 @@ contract StakingInterfaceMockV1 {
/**
* @notice Contract for staking contract tests
**/
*/
contract StakingInterfaceMockV2 {
function () external payable {
@ -145,7 +145,7 @@ contract StakingInterfaceMockV2 {
/**
* @dev Interface that could be destroyed by selfdestruct
**/
*/
contract DestroyableStakingInterface {
function method() public pure returns (uint256) {

View File

@ -7,7 +7,7 @@ import "contracts/NuCypherToken.sol";
/**
* @notice Upgrade to this contract must lead to fail
**/
*/
contract StakingEscrowBad is StakingEscrow {
constructor(
@ -45,7 +45,7 @@ contract StakingEscrowBad is StakingEscrow {
/**
* @notice Contract for testing upgrading the StakingEscrow contract
**/
*/
contract StakingEscrowV2Mock is StakingEscrow {
uint256 public valueToCheck;
@ -97,7 +97,7 @@ contract StakingEscrowV2Mock is StakingEscrow {
/**
* @notice Contract for testing staking escrow contract
**/
*/
contract PolicyManagerForStakingEscrowMock {
StakingEscrow public escrow;
@ -113,21 +113,21 @@ contract PolicyManagerForStakingEscrowMock {
/**
* @notice Update node info
**/
*/
function updateReward(address _node, uint16 _period) external {
nodes[_node].push(_period);
}
/**
* @notice Get length of array
**/
*/
function getPeriodsLength(address _node) public view returns (uint256) {
return nodes[_node].length;
}
/**
* @notice Get period info
**/
*/
function getPeriod(address _node, uint256 _index) public view returns (uint16) {
return nodes[_node][_index];
}
@ -137,7 +137,7 @@ contract PolicyManagerForStakingEscrowMock {
/**
* @notice Contract for testing staking escrow contract
**/
*/
contract AdjudicatorForStakingEscrowMock {
StakingEscrow public escrow;
@ -160,7 +160,7 @@ contract AdjudicatorForStakingEscrowMock {
/**
* @notice Intermediary contract for testing worker
**/
*/
contract Intermediary {
NuCypherToken token;
@ -189,7 +189,7 @@ contract Intermediary {
/**
* @notice Contract for testing staking escrow contract
**/
*/
contract WorkLockForStakingEscrowMock {
StakingEscrow public escrow;

View File

@ -6,7 +6,7 @@ import "contracts/NuCypherToken.sol";
/**
* @notice Contract for using in WorkLock tests
**/
*/
contract StakingEscrowForWorkLockMock {
struct StakerInfo {

View File

@ -7,7 +7,7 @@ import "contracts/proxy/Upgradeable.sol";
/**
* @dev This contract can't be target for dispatcher because missed `previousTarget`
**/
*/
contract BadDispatcherStorage {
address public owner;
@ -24,7 +24,7 @@ contract BadDispatcherStorage {
/**
* @dev Upgrade to this contract will fail because added `fakeValue`
**/
*/
contract ContractV2BadStorage is Upgradeable {
// TODO can't catch such a violation
@ -59,7 +59,7 @@ contract ContractV2BadStorage is Upgradeable {
/**
* @dev Upgrade to this contract will fail because `verifyState` is broken
**/
*/
contract ContractV2BadVerifyState is ContractV1(1) {
function verifyState(address) public {

View File

@ -6,7 +6,7 @@ import "contracts/proxy/Upgradeable.sol";
/**
* @dev Base contract for testing upgrading using dispatcher
**/
*/
contract ContractV1 is Upgradeable {
event EventV1(uint256 value);

View File

@ -6,7 +6,7 @@ import "contracts/proxy/Upgradeable.sol";
/**
* @dev Extension of the contract using valid storage variables
**/
*/
contract ContractV2 is Upgradeable {
event EventV2(uint8 value);
@ -176,7 +176,7 @@ contract ContractV2 is Upgradeable {
/**
* @dev Get array by one parameter.
**/
*/
function delegateGetArray(
address _target,
string memory _signature,

View File

@ -10,7 +10,7 @@ import "contracts/proxy/Upgradeable.sol";
* This demonstrates how to mitigate possible changes in the compiler while using the proxy pattern
* Many methods are not optimized on purpose to increase readability
* see https://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
**/
*/
contract ContractV4 is Upgradeable {
// slot allocation costs nothing

View File

@ -6,7 +6,7 @@ import "contracts/proxy/Upgradeable.sol";
/**
* @dev Contract that could be destroyed by selfdestruct
**/
*/
contract Destroyable is Upgradeable {
uint256 public constructorValue;

View File

@ -0,0 +1,144 @@
"""
This file is part of nucypher.
nucypher is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
nucypher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import os
import pytest
import requests
from eth_utils import keccak
from nucypher.blockchain.eth.deployers import NucypherTokenDeployer, StakingEscrowDeployer, PolicyManagerDeployer, \
AdjudicatorDeployer, BaseContractDeployer, UpgradeableContractMixin, DispatcherDeployer
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory, BlockchainDeployerInterface
from nucypher.blockchain.eth.registry import InMemoryContractRegistry, BaseContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler, SourceDirs
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.blockchain import TesterBlockchain
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD, STAKING_ESCROW_DEPLOYMENT_SECRET, \
POLICY_MANAGER_DEPLOYMENT_SECRET, ADJUDICATOR_DEPLOYMENT_SECRET
USER = "nucypher"
REPO = "nucypher"
BRANCH = "master"
GITHUB_SOURCE_LINK = f"https://api.github.com/repos/{USER}/{REPO}/contents/nucypher/blockchain/eth/sol/source?ref={BRANCH}"
def download_github_dir(source_link: str, target_folder: str):
response = requests.get(source_link)
if response.status_code != 200:
error = f"Failed to call api {source_link} with status code {response.status_code}"
raise RuntimeError(error)
for content in response.json():
path = os.path.join(target_folder, content["name"])
if content["type"] == "dir":
os.mkdir(path)
download_github_dir(content["url"], path)
else:
download_github_file(content["download_url"], path)
def download_github_file(source_link: str, target_folder: str):
response = requests.get(source_link)
if response.status_code != 200:
error = f"Failed to call api {source_link} with status code {response.status_code}"
raise RuntimeError(error)
raw_data = response.content
with open(target_folder, 'wb') as registry_file:
registry_file.seek(0)
registry_file.write(raw_data)
registry_file.truncate()
# Constructor parameters overrides for previous versions if needed
# 'None' value removes arg from list of constructor parameters
CONSTRUCTOR_OVERRIDES = {
# Test example
StakingEscrowDeployer.contract_name: {"v0.0.0": {"_hoursPerPeriod": 1}}
}
def deploy_earliest_contract(blockchain_interface: BlockchainDeployerInterface,
deployer: BaseContractDeployer,
secret: str):
contract_name = deployer.contract_name
earliest_version, _data = blockchain_interface.find_raw_contract_data(contract_name, "earliest")
try:
overrides = CONSTRUCTOR_OVERRIDES[contract_name][earliest_version]
except KeyError:
overrides = dict()
deployer.deploy(secret_hash=keccak(text=secret), contract_version=earliest_version, **overrides)
def upgrade_to_latest_contract(deployer, secret: str):
old_secret = bytes(secret, encoding='utf-8')
new_secret_hash = keccak(b'new' + old_secret)
deployer.upgrade(existing_secret_plaintext=old_secret,
new_secret_hash=new_secret_hash,
contract_version="latest")
@pytest.mark.slow
def test_upgradeability(temp_dir_path, token_economics):
# Prepare remote source for compilation
download_github_dir(GITHUB_SOURCE_LINK, temp_dir_path)
solidity_compiler = SolidityCompiler(source_dirs=[SourceDirs(SolidityCompiler.default_contract_dir()),
SourceDirs(temp_dir_path)])
# Prepare the blockchain
blockchain_interface = BlockchainDeployerInterface(provider_uri='tester://pyevm/2', compiler=solidity_compiler)
blockchain_interface.connect()
origin = blockchain_interface.client.accounts[0]
BlockchainInterfaceFactory.register_interface(interface=blockchain_interface)
blockchain_interface.transacting_power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD, account=origin)
blockchain_interface.transacting_power.activate()
# Check contracts with multiple versions
raw_contracts = blockchain_interface._raw_contract_cache
contract_name = AdjudicatorDeployer.contract_name
test_adjudicator = len(raw_contracts[contract_name]) > 1
contract_name = StakingEscrowDeployer.contract_name
test_staking_escrow = len(raw_contracts[contract_name]) > 1
contract_name = PolicyManagerDeployer.contract_name
test_policy_manager = len(raw_contracts[contract_name]) > 1
if not test_adjudicator and not test_staking_escrow and not test_policy_manager:
return
# Prepare master version of contracts and upgrade to the latest
registry = InMemoryContractRegistry()
token_deployer = NucypherTokenDeployer(registry=registry, deployer_address=origin)
token_deployer.deploy()
staking_escrow_deployer = StakingEscrowDeployer(registry=registry, deployer_address=origin)
deploy_earliest_contract(blockchain_interface, staking_escrow_deployer, secret=STAKING_ESCROW_DEPLOYMENT_SECRET)
assert staking_escrow_deployer.contract.functions.secondsPerPeriod().call() == 3600
if test_staking_escrow:
upgrade_to_latest_contract(staking_escrow_deployer, secret=STAKING_ESCROW_DEPLOYMENT_SECRET)
assert staking_escrow_deployer.contract.functions.secondsPerPeriod().call() == token_economics.seconds_per_period
if test_policy_manager:
policy_manager_deployer = PolicyManagerDeployer(registry=registry, deployer_address=origin)
deploy_earliest_contract(blockchain_interface, policy_manager_deployer, secret=POLICY_MANAGER_DEPLOYMENT_SECRET)
upgrade_to_latest_contract(policy_manager_deployer, secret=POLICY_MANAGER_DEPLOYMENT_SECRET)
if test_adjudicator:
adjudicator_deployer = AdjudicatorDeployer(registry=registry, deployer_address=origin)
deploy_earliest_contract(blockchain_interface, adjudicator_deployer, secret=ADJUDICATOR_DEPLOYMENT_SECRET)
upgrade_to_latest_contract(adjudicator_deployer, secret=ADJUDICATOR_DEPLOYMENT_SECRET)

View File

@ -45,10 +45,10 @@ def test_deploy_ethereum_contracts(testerchain,
with pytest.raises(BaseContractDeployer.ContractDeploymentError):
assert token_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED
assert not token_deployer.is_deployed
assert not token_deployer.is_deployed()
token_deployer.deploy(progress=deployment_progress)
assert token_deployer.is_deployed
assert token_deployer.is_deployed()
assert len(token_deployer.contract_address) == 42
token_agent = NucypherTokenAgent(registry=test_registry)
@ -70,10 +70,10 @@ def test_deploy_ethereum_contracts(testerchain,
with pytest.raises(BaseContractDeployer.ContractDeploymentError):
assert staking_escrow_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED
assert not staking_escrow_deployer.is_deployed
assert not staking_escrow_deployer.is_deployed()
staking_escrow_deployer.deploy(secret_hash=keccak(stakers_escrow_secret), progress=deployment_progress)
assert staking_escrow_deployer.is_deployed
assert staking_escrow_deployer.is_deployed()
assert len(staking_escrow_deployer.contract_address) == 42
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
@ -97,10 +97,10 @@ def test_deploy_ethereum_contracts(testerchain,
with pytest.raises(BaseContractDeployer.ContractDeploymentError):
assert policy_manager_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED
assert not policy_manager_deployer.is_deployed
assert not policy_manager_deployer.is_deployed()
policy_manager_deployer.deploy(secret_hash=keccak(policy_manager_secret), progress=deployment_progress)
assert policy_manager_deployer.is_deployed
assert policy_manager_deployer.is_deployed()
assert len(policy_manager_deployer.contract_address) == 42
policy_agent = policy_manager_deployer.make_agent()
@ -124,10 +124,10 @@ def test_deploy_ethereum_contracts(testerchain,
with pytest.raises(BaseContractDeployer.ContractDeploymentError):
assert adjudicator_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED
assert not adjudicator_deployer.is_deployed
assert not adjudicator_deployer.is_deployed()
adjudicator_deployer.deploy(secret_hash=keccak(adjudicator_secret), progress=deployment_progress)
assert adjudicator_deployer.is_deployed
assert adjudicator_deployer.is_deployed()
assert len(adjudicator_deployer.contract_address) == 42
adjudicator_agent = adjudicator_deployer.make_agent()

View File

@ -83,7 +83,7 @@ def test_policy_manager_has_dispatcher(policy_manager_deployer, testerchain, tes
# Let's get the "bare" PolicyManager contract (i.e., unwrapped, no dispatcher)
existing_bare_contract = testerchain.get_contract_by_name(registry=test_registry,
name=policy_manager_deployer.contract_name,
contract_name=policy_manager_deployer.contract_name,
proxy_name=DispatcherDeployer.contract_name,
use_proxy_address=False)
@ -106,7 +106,7 @@ def test_upgrade(testerchain, test_registry):
deployer_address=testerchain.etherbase_account)
bare_contract = testerchain.get_contract_by_name(registry=test_registry,
name=PolicyManagerDeployer.contract_name,
contract_name=PolicyManagerDeployer.contract_name,
proxy_name=DispatcherDeployer.contract_name,
use_proxy_address=False)
old_address = bare_contract.address
@ -116,10 +116,11 @@ def test_upgrade(testerchain, test_registry):
new_secret_hash=new_secret_hash)
receipts = deployer.upgrade(existing_secret_plaintext=old_secret,
new_secret_hash=new_secret_hash)
new_secret_hash=new_secret_hash,
ignore_deployed=True)
bare_contract = testerchain.get_contract_by_name(registry=test_registry,
name=PolicyManagerDeployer.contract_name,
contract_name=PolicyManagerDeployer.contract_name,
proxy_name=DispatcherDeployer.contract_name,
use_proxy_address=False)
@ -146,7 +147,8 @@ def test_rollback(testerchain, test_registry):
# Let's do one more upgrade
receipts = deployer.upgrade(existing_secret_plaintext=old_secret,
new_secret_hash=new_secret_hash)
new_secret_hash=new_secret_hash,
ignore_deployed=True)
for title, receipt in receipts.items():
assert receipt['status'] == 1

View File

@ -80,7 +80,7 @@ def test_deploy_multiple_preallocations(testerchain, test_registry):
testerchain = testerchain
deployer_account = testerchain.etherbase_account
router = testerchain.get_contract_by_name(registry=test_registry, name=StakingInterfaceRouterDeployer.contract_name)
router = testerchain.get_contract_by_name(registry=test_registry, contract_name=StakingInterfaceRouterDeployer.contract_name)
router_address = router.address
for index in range(NUMBER_OF_PREALLOCATIONS):
deployer = PreallocationEscrowDeployer(deployer_address=deployer_account, registry=test_registry)
@ -108,10 +108,10 @@ def test_upgrade_staking_interface(testerchain, test_registry):
old_secret = INSECURE_DEPLOYMENT_SECRET_PLAINTEXT
new_secret = 'new' + STAKING_INTERFACE_DEPLOYMENT_SECRET
new_secret_hash = keccak_digest(new_secret.encode())
router = testerchain.get_contract_by_name(registry=test_registry, name=StakingInterfaceRouterDeployer.contract_name)
router = testerchain.get_contract_by_name(registry=test_registry, contract_name=StakingInterfaceRouterDeployer.contract_name)
contract = testerchain.get_contract_by_name(registry=test_registry,
name=StakingInterfaceDeployer.contract_name,
contract_name=StakingInterfaceDeployer.contract_name,
proxy_name=StakingInterfaceRouterDeployer.contract_name,
use_proxy_address=False)
@ -122,7 +122,8 @@ def test_upgrade_staking_interface(testerchain, test_registry):
registry=test_registry)
receipts = staking_interface_deployer.upgrade(existing_secret_plaintext=old_secret,
new_secret_hash=new_secret_hash)
new_secret_hash=new_secret_hash,
ignore_deployed=True)
assert len(receipts) == 2
@ -135,7 +136,7 @@ def test_upgrade_staking_interface(testerchain, test_registry):
new_target = router.functions.target().call()
contract = testerchain.get_contract_by_name(registry=test_registry,
name=StakingInterfaceDeployer.contract_name,
contract_name=StakingInterfaceDeployer.contract_name,
proxy_name=StakingInterfaceRouterDeployer.contract_name,
use_proxy_address=False)
assert new_target == contract.address

View File

@ -66,7 +66,7 @@ def test_staking_escrow_has_dispatcher(staking_escrow_deployer, testerchain, tes
# Let's get the "bare" StakingEscrow contract (i.e., unwrapped, no dispatcher)
existing_bare_contract = testerchain.get_contract_by_name(registry=test_registry,
name=staking_escrow_deployer.contract_name,
contract_name=staking_escrow_deployer.contract_name,
proxy_name=DispatcherDeployer.contract_name,
use_proxy_address=False)
@ -92,7 +92,9 @@ def test_upgrade(testerchain, test_registry, token_economics):
with pytest.raises(deployer.ContractDeploymentError):
deployer.upgrade(existing_secret_plaintext=wrong_secret, new_secret_hash=new_secret_hash)
receipts = deployer.upgrade(existing_secret_plaintext=old_secret, new_secret_hash=new_secret_hash)
receipts = deployer.upgrade(existing_secret_plaintext=old_secret,
new_secret_hash=new_secret_hash,
ignore_deployed=True)
for title, receipt in receipts.items():
assert receipt['status'] == 1
@ -108,7 +110,9 @@ def test_rollback(testerchain, test_registry):
current_target = staking_agent.contract.functions.target().call()
# Let's do one more upgrade
receipts = deployer.upgrade(existing_secret_plaintext=old_secret, new_secret_hash=new_secret_hash)
receipts = deployer.upgrade(existing_secret_plaintext=old_secret,
new_secret_hash=new_secret_hash,
ignore_deployed=True)
for title, receipt in receipts.items():
assert receipt['status'] == 1
@ -145,7 +149,7 @@ def test_deploy_bare_upgradeable_contract_deployment(testerchain, test_registry,
old_number_of_enrollments = enrolled_names.count(StakingEscrowDeployer.contract_name)
old_number_of_proxy_enrollments = enrolled_names.count(StakingEscrowDeployer._proxy_deployer.contract_name)
receipts = deployer.deploy(initial_deployment=False)
receipts = deployer.deploy(initial_deployment=False, ignore_deployed=True)
for title, receipt in receipts.items():
assert receipt['status'] == 1

View File

@ -0,0 +1,10 @@
pragma solidity ^0.5.3;
/**
* @notice Test extracting version number from dev docs
* @dev |v1.1.4|
*/
contract VersionTest {
uint16 public constant VERSION = 1;
}

View File

@ -0,0 +1,10 @@
pragma solidity ^0.5.3;
/**
* @notice Test extracting version number from dev docs
* @dev |v1.2.3|
*/
contract VersionTest {
uint16 public constant VERSION = 2;
}

View File

@ -14,26 +14,29 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import os
from os.path import dirname, abspath
import pytest
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
# Prevents TesterBlockchain to be picked up by py.test as a test class
from nucypher.blockchain.eth.sol.compile import SolidityCompiler, SourceDirs
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.blockchain import TesterBlockchain as _TesterBlockchain
from nucypher.utilities.sandbox.constants import (
DEVELOPMENT_ETH_AIRDROP_AMOUNT,
NUMBER_OF_ETH_TEST_ACCOUNTS,
NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS,
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS,
TEST_PROVIDER_URI
)
TEST_PROVIDER_URI,
INSECURE_DEVELOPMENT_PASSWORD)
@pytest.fixture()
def another_testerchain(solidity_compiler):
testerchain = _TesterBlockchain(eth_airdrop=True, free_transactions=True, light=True)
testerchain = _TesterBlockchain(eth_airdrop=True, free_transactions=True, light=True, compiler=solidity_compiler)
testerchain.deployer_address = testerchain.etherbase_account
assert testerchain.is_light
yield testerchain
@ -86,3 +89,71 @@ def test_testerchain_creation(testerchain, another_testerchain):
tx = {'to': etherbase, 'from': account, 'value': 100}
txhash = chain.client.send_transaction(tx)
_receipt = chain.wait_for_receipt(txhash)
def test_multiversion_contract():
# Prepare compiler
base_dir = os.path.join(dirname(abspath(__file__)), "contracts", "multiversion")
v1_dir = os.path.join(base_dir, "v1")
v2_dir = os.path.join(base_dir, "v2")
root_dir = SolidityCompiler.default_contract_dir()
solidity_compiler = SolidityCompiler(source_dirs=[SourceDirs(root_dir, {v2_dir}),
SourceDirs(root_dir, {v1_dir})])
# Prepare chain
blockchain_interface = BlockchainDeployerInterface(provider_uri='tester://pyevm/2', compiler=solidity_compiler)
blockchain_interface.connect()
origin = blockchain_interface.client.accounts[0]
blockchain_interface.transacting_power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD, account=origin)
blockchain_interface.transacting_power.activate()
# Searching both contract through raw data
contract_name = "VersionTest"
requested_version = "v1.2.3"
version, _data = blockchain_interface.find_raw_contract_data(contract_name=contract_name,
requested_version=requested_version)
assert version == requested_version
version, _data = blockchain_interface.find_raw_contract_data(contract_name=contract_name,
requested_version="latest")
assert version == requested_version
requested_version = "v1.1.4"
version, _data = blockchain_interface.find_raw_contract_data(contract_name=contract_name,
requested_version=requested_version)
assert version == requested_version
version, _data = blockchain_interface.find_raw_contract_data(contract_name=contract_name,
requested_version="earliest")
assert version == requested_version
# Deploy different contracts and check their versions
registry = InMemoryContractRegistry()
contract, receipt = blockchain_interface.deploy_contract(deployer_address=origin,
registry=registry,
contract_name=contract_name,
contract_version="v1.1.4")
assert contract.version == "v1.1.4"
assert contract.functions.VERSION().call() == 1
contract, receipt = blockchain_interface.deploy_contract(deployer_address=origin,
registry=registry,
contract_name=contract_name,
contract_version="earliest")
assert contract.version == "v1.1.4"
assert contract.functions.VERSION().call() == 1
contract, receipt = blockchain_interface.deploy_contract(deployer_address=origin,
registry=registry,
contract_name=contract_name,
contract_version="v1.2.3")
assert contract.version == "v1.2.3"
assert contract.functions.VERSION().call() == 2
contract, receipt = blockchain_interface.deploy_contract(deployer_address=origin,
registry=registry,
contract_name=contract_name,
contract_version="latest")
assert contract.version == "v1.2.3"
assert contract.functions.VERSION().call() == 2
contract, receipt = blockchain_interface.deploy_contract(deployer_address=origin,
registry=registry,
contract_name=contract_name)
assert contract.version == "v1.2.3"
assert contract.functions.VERSION().call() == 2

View File

@ -47,28 +47,32 @@ def test_contract_registry(tempfile_path):
test_name = 'TestContract'
test_addr = '0xDEADBEEF'
test_abi = ['fake', 'data']
test_version = "some_version"
test_registry.enroll(contract_name=test_name,
contract_address=test_addr,
contract_abi=test_abi)
contract_abi=test_abi,
contract_version=test_version)
# Search by name...
contract_records = test_registry.search(contract_name=test_name)
assert len(contract_records) == 1, 'More than one record for {}'.format(test_name)
assert len(contract_records[0]) == 3, 'Registry record is the wrong length'
name, address, abi = contract_records[0]
assert len(contract_records[0]) == 4, 'Registry record is the wrong length'
name, version, address, abi = contract_records[0]
assert name == test_name
assert address == test_addr
assert abi == test_abi
assert version == test_version
# ...or by address
contract_record = test_registry.search(contract_address=test_addr)
name, address, abi = contract_record
name, version, address, abi = contract_record
assert name == test_name
assert address == test_addr
assert abi == test_abi
assert version == test_version
# Check that searching for an unknown contract raises
with pytest.raises(BaseContractRegistry.UnknownContract):

View File

@ -14,9 +14,12 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import os
from os.path import dirname, abspath
from nucypher.blockchain.eth.deployers import NucypherTokenDeployer
from nucypher.blockchain.eth.sol.compile import SolidityCompiler, SourceDirs
from nucypher.utilities.sandbox.blockchain import TesterBlockchain
def test_nucypher_contract_compiled(testerchain, test_registry):
@ -24,4 +27,41 @@ def test_nucypher_contract_compiled(testerchain, test_registry):
origin, *everybody_else = testerchain.client.accounts
token_contract_identifier = NucypherTokenDeployer(registry=test_registry, deployer_address=origin).contract_name
assert token_contract_identifier in testerchain._BlockchainDeployerInterface__raw_contract_cache
assert token_contract_identifier in testerchain._raw_contract_cache
token_data = testerchain._raw_contract_cache[token_contract_identifier]
assert len(token_data) == 1
assert "v0.0.0" in token_data
def test_multi_source_compilation(testerchain):
solidity_compiler = SolidityCompiler(source_dirs=[
(SolidityCompiler.default_contract_dir(), None),
(SolidityCompiler.default_contract_dir(), {TesterBlockchain.TEST_CONTRACTS_DIR})
])
interfaces = solidity_compiler.compile()
# Remove AST because id in tree node depends on compilation scope
for contract_name, contract_data in interfaces.items():
for version, data in contract_data.items():
data.pop("ast")
raw_cache = testerchain._raw_contract_cache.copy()
for contract_name, contract_data in raw_cache.items():
for version, data in contract_data.items():
data.pop("ast")
assert interfaces == raw_cache
def test_multi_versions():
base_dir = os.path.join(dirname(abspath(__file__)), "contracts", "multiversion")
v1_dir = os.path.join(base_dir, "v1")
v2_dir = os.path.join(base_dir, "v2")
root_dir = SolidityCompiler.default_contract_dir()
solidity_compiler = SolidityCompiler(source_dirs=[SourceDirs(root_dir, {v1_dir}),
SourceDirs(root_dir, {v2_dir})])
interfaces = solidity_compiler.compile()
assert "VersionTest" in interfaces
contract_data = interfaces["VersionTest"]
assert len(contract_data) == 2
assert "v1.2.3" in contract_data
assert "v1.1.4" in contract_data
assert contract_data["v1.2.3"]["devdoc"] != contract_data["v1.1.4"]["devdoc"]

View File

@ -32,19 +32,13 @@ def generate_insecure_secret() -> str:
return formatted_secret
# TODO: Use temp module
DEPLOYMENT_REGISTRY_FILEPATH = os.path.join('/', 'tmp', 'nucypher-test-autodeploy.json')
PLANNED_UPGRADES = 4
INSECURE_SECRETS = {v: generate_insecure_secret() for v in range(1, PLANNED_UPGRADES+1)}
@pytest.fixture(scope="module")
def registry_filepath():
if os.path.exists(DEPLOYMENT_REGISTRY_FILEPATH):
os.remove(DEPLOYMENT_REGISTRY_FILEPATH)
yield DEPLOYMENT_REGISTRY_FILEPATH
if os.path.exists(DEPLOYMENT_REGISTRY_FILEPATH):
os.remove(DEPLOYMENT_REGISTRY_FILEPATH)
def registry_filepath(temp_dir_path):
return os.path.join(temp_dir_path, 'nucypher-test-autodeploy.json')
def test_nucypher_deploy_contracts(click_runner,
@ -90,7 +84,7 @@ def test_nucypher_deploy_contracts(click_runner,
# Read several records
token_record, escrow_record, dispatcher_record, *other_records = registry_data
registered_name, registered_address, registered_abi = token_record
registered_name, registered_version, registered_address, registered_abi = token_record
#
# Agency
@ -101,6 +95,7 @@ def test_nucypher_deploy_contracts(click_runner,
assert token_agent.contract_name == registered_name
assert token_agent.registry_contract_name == registered_name
assert token_agent.contract_address == registered_address
assert token_agent.contract.version == registered_version
# Now show that we can use contract Agency and read from the blockchain
assert token_agent.get_balance() == 0
@ -214,7 +209,7 @@ def test_upgrade_contracts(click_runner, registry_filepath, testerchain):
proxy_name = 'Dispatcher'
registry = LocalContractRegistry(filepath=registry_filepath)
real_old_contract = testerchain.get_contract_by_name(name=contract_name,
real_old_contract = testerchain.get_contract_by_name(contract_name=contract_name,
registry=registry,
proxy_name=proxy_name,
use_proxy_address=False)
@ -227,7 +222,7 @@ def test_upgrade_contracts(click_runner, registry_filepath, testerchain):
assert targeted_address == real_old_contract.address
# Assemble CLI command
command = (cli_action, '--contract-name', contract_name, *base_command)
command = (cli_action, '--contract-name', contract_name, '--ignore-deployed', *base_command)
# Select upgrade interactive input scenario
current_version = version_tracker[contract_name]
@ -268,8 +263,8 @@ def test_upgrade_contracts(click_runner, registry_filepath, testerchain):
assert len(records) == contract_enrollments, error
old, new = records[-2:] # Get the last two entries
old_name, old_address, *abi = old # Previous version
new_name, new_address, *abi = new # New version
old_name, _old_version, old_address, *abi = old # Previous version
new_name, _new_version, new_address, *abi = new # New version
assert old_address == real_old_contract.address
assert old_name == new_name # TODO: Inspect ABI / Move to different test.
@ -319,8 +314,8 @@ def test_rollback(click_runner, testerchain, registry_filepath):
*old_records, v3, v4 = records
current_target, rollback_target = v4, v3
_name, current_target_address, *abi = current_target
_name, rollback_target_address, *abi = rollback_target
_name, _version, current_target_address, *abi = current_target
_name, _version, rollback_target_address, *abi = rollback_target
assert current_target_address != rollback_target_address
# Ensure the proxy targets the rollback target (previous version)

View File

@ -117,7 +117,8 @@ def test_bare_contract_deployment_to_alternate_registry(click_runner, test_regis
'--provider', TEST_PROVIDER_URI,
'--registry-infile', MOCK_REGISTRY_FILEPATH,
'--registry-outfile', ALTERNATE_REGISTRY_FILEPATH,
'--poa')
'--poa',
'--ignore-deployed')
user_input = '0\n' + 'Y\n' + 'DEPLOY'
result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False)