mirror of https://github.com/nucypher/nucypher.git
Merge pull request #1470 from vzotova/upgradability-test
Versioning of contracts and upgradability testpull/1542/head
commit
11e7fc6c92
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ pragma solidity ^0.5.3;
|
|||
|
||||
/**
|
||||
* @notice Contract for using in token tests
|
||||
**/
|
||||
*/
|
||||
contract ReceiveApprovalMethodMock {
|
||||
|
||||
address public sender;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -6,7 +6,7 @@ import "contracts/NuCypherToken.sol";
|
|||
|
||||
/**
|
||||
* @notice Contract for using in WorkLock tests
|
||||
**/
|
||||
*/
|
||||
contract StakingEscrowForWorkLockMock {
|
||||
|
||||
struct StakerInfo {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -6,7 +6,7 @@ import "contracts/proxy/Upgradeable.sol";
|
|||
|
||||
/**
|
||||
* @dev Contract that could be destroyed by selfdestruct
|
||||
**/
|
||||
*/
|
||||
contract Destroyable is Upgradeable {
|
||||
|
||||
uint256 public constructorValue;
|
||||
|
|
|
@ -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)
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue