New deployment order: StakingEscrow dispatcher, then PolicyManager+Dispatcher -> Adjudicator+Dispatcher -> WorkLock and only after this StakingEscrow itself

pull/2518/head
vzotova 2021-01-24 15:39:49 +03:00
parent 9815440464
commit 61d8dc1ec0
31 changed files with 496 additions and 919 deletions

View File

@ -179,9 +179,9 @@ class ContractAdministrator(NucypherTokenActor):
)
dispatched_upgradeable_deployer_classes = (
StakingEscrowDeployer,
PolicyManagerDeployer,
AdjudicatorDeployer,
StakingEscrowDeployer,
)
upgradeable_deployer_classes = (

View File

@ -227,11 +227,8 @@ class StakingEscrowAgent(EthereumContractAgent):
contract_name: str = STAKING_ESCROW_CONTRACT_NAME
_proxy_name: str = DISPATCHER_CONTRACT_NAME
_excluded_interfaces = (
'setPolicyManager',
'verifyState',
'finishUpgrade',
'setAdjudicator',
'setWorkLock'
'finishUpgrade'
)
DEFAULT_PAGINATION_SIZE: int = 30 # TODO: Use dynamic pagination size (see #1424)

View File

@ -23,6 +23,7 @@ DISPATCHER_CONTRACT_NAME = 'Dispatcher'
STAKING_INTERFACE_ROUTER_CONTRACT_NAME = "StakingInterfaceRouter"
NUCYPHER_TOKEN_CONTRACT_NAME = 'NuCypherToken'
STAKING_ESCROW_CONTRACT_NAME = 'StakingEscrow'
STAKING_ESCROW_STUB_CONTRACT_NAME = 'StakingEscrowStub'
POLICY_MANAGER_CONTRACT_NAME = 'PolicyManager'
STAKING_INTERFACE_CONTRACT_NAME = 'StakingInterface'
PREALLOCATION_ESCROW_CONTRACT_NAME = 'PreallocationEscrow'

View File

@ -18,7 +18,7 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
from collections import OrderedDict
from constant_sorrow.constants import (BARE, CONTRACT_NOT_DEPLOYED, FULL, IDLE, NO_BENEFICIARY, NO_DEPLOYER_CONFIGURED)
from constant_sorrow.constants import (BARE, CONTRACT_NOT_DEPLOYED, FULL, IDLE, INIT, NO_BENEFICIARY, NO_DEPLOYER_CONFIGURED)
from eth_typing.evm import ChecksumAddress
from typing import Dict, List, Tuple
from web3 import Web3
@ -505,15 +505,13 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
agency = StakingEscrowAgent
contract_name = agency.contract_name
contract_name_stub = "StakingEscrowStub"
can_be_idle = True
preparation_steps = ('contract_deployment',
'dispatcher_deployment',
'set_on_policy_manager',
'set_on_adjudicator')
worklock_preparation_step = ('set_on_worklock',)
init_steps = ('stub_deployment', 'dispatcher_deployment')
preparation_steps = ('contract_deployment', 'dispatcher_retarget')
activation_steps = ('approve_reward_transfer', 'initialize')
deployment_steps = preparation_steps + worklock_preparation_step + activation_steps
deployment_steps = preparation_steps + activation_steps
_proxy_deployer = DispatcherDeployer
def __init__(self, *args, **kwargs):
@ -528,18 +526,48 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
worklock_contract_name = WorklockDeployer.contract_name
self.token_contract = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=token_contract_name)
self.policy_manager = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=policy_manager_contract_name,
proxy_name=policy_manager_proxy_name)
self.adjudicator = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=adjudicator_contract_name,
proxy_name=adjudicator_proxy_name)
try:
self.policy_manager = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=policy_manager_contract_name,
proxy_name=policy_manager_proxy_name)
except self.registry.UnknownContract:
self.policy_manager = None
try:
self.adjudicator = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=adjudicator_contract_name,
proxy_name=adjudicator_proxy_name)
except self.registry.UnknownContract:
self.adjudicator = None
try:
self.worklock = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=worklock_contract_name)
except self.registry.UnknownContract:
self.worklock = None
def _deploy_stub(self, gas_limit: int = None, confirmations: int = 0, **overrides):
constructor_kwargs = {
"_hoursPerPeriod": self.economics.hours_per_period,
"_minLockedPeriods": self.economics.minimum_locked_periods,
"_minAllowableLockedTokens": self.economics.minimum_allowed_locked,
"_maxAllowableLockedTokens": self.economics.maximum_allowed_locked
}
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_stub,
gas_limit=gas_limit,
confirmations=confirmations,
**constructor_kwargs
)
return the_escrow_contract, deploy_receipt
def _deploy_essential(self, contract_version: str, gas_limit: int = None, confirmations: int = 0, **overrides):
args = self.economics.staking_deployment_parameters
constructor_kwargs = {
@ -557,7 +585,7 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
}
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
# Force use of the contract addresses from the registry
constructor_kwargs.update({"_token": self.token_contract.address,
"_policyManager": self.policy_manager.address,
"_adjudicator": self.adjudicator.address,
@ -575,7 +603,7 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
return the_escrow_contract, deploy_receipt
def deploy(self,
deployment_mode=FULL,
deployment_mode=INIT,
gas_limit: int = None,
progress=None,
contract_version: str = "latest",
@ -597,7 +625,7 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
Returns transaction receipts in a dict.
"""
if deployment_mode not in (BARE, IDLE, FULL):
if deployment_mode not in (BARE, IDLE, INIT, FULL):
raise ValueError(f"Invalid deployment mode ({deployment_mode})")
# Raise if not all-systems-go
@ -608,32 +636,60 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
if gas_limit:
origin_args.update({'gas': gas_limit}) # TODO: Gas Management - #842
# 1 - Deploy #
if emitter:
emitter.message(f"\nNext Transaction: {self.contract_name} Contract Creation", color='blue', bold=True)
the_escrow_contract, deploy_receipt = self._deploy_essential(contract_version=contract_version,
gas_limit=gas_limit,
confirmations=confirmations,
**overrides)
if deployment_mode is INIT:
# 1 - Deploy Stub
if emitter:
emitter.message(f"\nNext Transaction: {self.contract_name_stub} Contract Creation", color='blue', bold=True)
the_escrow_contract, deploy_receipt = self._deploy_stub(gas_limit=gas_limit,
confirmations=confirmations,
**overrides)
else:
# 1 - Deploy StakingEscrow
if emitter:
emitter.message(f"\nNext Transaction: {self.contract_name} Contract Creation", color='blue', bold=True)
the_escrow_contract, deploy_receipt = self._deploy_essential(contract_version=contract_version,
gas_limit=gas_limit,
confirmations=confirmations,
**overrides)
# This is the end of bare deployment.
if deployment_mode is BARE:
self._contract = the_escrow_contract
receipts = self._finish_bare_deployment(deployment_receipt=deploy_receipt, progress=progress)
return receipts
# This is the end of bare deployment.
if deployment_mode is BARE:
self._contract = the_escrow_contract
receipts = self._finish_bare_deployment(deployment_receipt=deploy_receipt, progress=progress)
return receipts
if progress:
progress.update(1)
# 2 - Deploy the dispatcher used for updating this contract #
if emitter:
emitter.message(f"\nNext Transaction: {DispatcherDeployer.contract_name} Contract Creation for {self.contract_name}", color='blue', bold=True)
dispatcher_deployer = DispatcherDeployer(registry=self.registry,
target_contract=the_escrow_contract,
deployer_address=self.deployer_address)
if deployment_mode is INIT:
# 2 - Deploy the dispatcher used for updating this contract #
if emitter:
emitter.message(f"\nNext Transaction: {DispatcherDeployer.contract_name} "
f"Contract Creation for {self.contract_name}",
color='blue', bold=True)
dispatcher_deployer = DispatcherDeployer(registry=self.registry,
target_contract=the_escrow_contract,
deployer_address=self.deployer_address)
dispatcher_receipts = dispatcher_deployer.deploy(gas_limit=gas_limit, confirmations=confirmations)
dispatcher_deploy_receipt = dispatcher_receipts[dispatcher_deployer.deployment_steps[0]]
else:
# 2 - Upgrade dispatcher to the real contract
if emitter:
emitter.message(f"\nNext Transaction: {DispatcherDeployer.contract_name} "
f"Contract Upgrade for {self.contract_name}",
color='blue', bold=True)
the_stub_contract = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=self.contract_name_stub)
dispatcher_deployer = DispatcherDeployer(registry=self.registry,
target_contract=the_stub_contract,
deployer_address=self.deployer_address,
bare=True)
dispatcher_retarget_receipt = dispatcher_deployer.retarget(new_target=the_escrow_contract.address,
gas_limit=gas_limit,
confirmations=confirmations)
dispatcher_receipts = dispatcher_deployer.deploy(gas_limit=gas_limit, confirmations=confirmations)
dispatcher_deploy_receipt = dispatcher_receipts[dispatcher_deployer.deployment_steps[0]]
if progress:
progress.update(1)
@ -645,57 +701,17 @@ class StakingEscrowDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
wrapped_escrow_contract = self.blockchain._wrap_contract(dispatcher_contract,
target_contract=the_escrow_contract)
# Configure the PolicyManager, Adjudicator, WorkLock contracts by setting the StakingEscrow
tx_args = {}
if gas_limit:
tx_args.update({'gas': gas_limit}) # TODO: #842
if emitter:
emitter.message(f"\nNext Transaction: Set StakingEscrow on {self.policy_manager.address}", color='blue', bold=True)
set_on_policy_manager_function = self.policy_manager.functions.setStakingEscrow(wrapped_escrow_contract.address)
set_on_policy_manager_receipt = self.blockchain.send_transaction(contract_function=set_on_policy_manager_function,
sender_address=self.deployer_address,
confirmations=confirmations,
payload=tx_args)
if progress:
progress.update(1)
if emitter:
emitter.message(f"\nNext Transaction: Set StakingEscrow on {self.adjudicator.address}", color='blue', bold=True)
set_on_adjudicator_function = self.adjudicator.functions.setStakingEscrow(wrapped_escrow_contract.address)
set_on_adjudicator_receipt = self.blockchain.send_transaction(contract_function=set_on_adjudicator_function,
sender_address=self.deployer_address,
confirmations=confirmations,
payload=tx_args)
if progress:
progress.update(1)
preparation_receipts = dict(zip(self.preparation_steps,
(deploy_receipt,
dispatcher_deploy_receipt,
set_on_policy_manager_receipt,
set_on_adjudicator_receipt)))
self.deployment_receipts = preparation_receipts
if self.worklock is not None:
if emitter:
emitter.message(f"\nNext Transaction: Set StakingEscrow on {self.worklock.address}", color='blue', bold=True)
set_on_worklock_function = self.worklock.functions.setStakingEscrow(wrapped_escrow_contract.address)
set_on_worklock_receipt = self.blockchain.send_transaction(contract_function=set_on_worklock_function,
sender_address=self.deployer_address,
confirmations=confirmations,
payload=tx_args)
receipts = dict(zip(self.worklock_preparation_step, (set_on_worklock_receipt,)))
self.deployment_receipts.update(receipts)
if progress:
progress.update(1)
# Switch the contract for the wrapped one
self._contract = wrapped_escrow_contract
if deployment_mode is INIT:
preparation_receipts = dict(zip(self.init_steps, (deploy_receipt, dispatcher_deploy_receipt)))
else:
preparation_receipts = dict(zip(self.preparation_steps, (deploy_receipt, dispatcher_retarget_receipt)))
self.deployment_receipts = preparation_receipts
# 3 & 4 - Activation
if deployment_mode is IDLE:
if deployment_mode is IDLE or deployment_mode is INIT:
# This is the end of deployment without activation: the contract is now idle, waiting for activation
return preparation_receipts
else: # deployment_mode is FULL
@ -788,24 +804,21 @@ class PolicyManagerDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
contract_name=staking_contract_name,
proxy_name=proxy_name)
except self.registry.UnknownContract:
self.staking_contract = None
staking_contract_name = StakingEscrowDeployer.contract_name_stub
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=staking_contract_name,
proxy_name=proxy_name)
def check_deployment_readiness(self, *args, **kwargs) -> Tuple[bool, list]:
policy_manager_deployment_rules = []
if self.staking_contract is not None:
staking_escrow_owner = self.staking_contract.functions.owner().call()
policy_manager_deployment_rules.append(
(self.deployer_address == staking_escrow_owner,
f'{self.contract_name} must be deployed by the owner of {STAKING_ESCROW_CONTRACT_NAME} ({staking_escrow_owner})')
)
staking_escrow_owner = self.staking_contract.functions.owner().call()
policy_manager_deployment_rules = [
(self.deployer_address == staking_escrow_owner,
f'{self.contract_name} must be deployed by the owner of {STAKING_ESCROW_CONTRACT_NAME} ({staking_escrow_owner})')
]
return super().check_deployment_readiness(additional_rules=policy_manager_deployment_rules, *args, **kwargs)
def _deploy_essential(self, contract_version: str, gas_limit: int = None, confirmations: int = 0, **overrides) -> tuple:
escrow_address = self.staking_contract.address if self.staking_contract is not None else NULL_ADDRESS
constructor_kwargs = {"_secondsPerPeriod": self.economics.seconds_per_period}
constructor_kwargs.update(overrides)
constructor_kwargs = {k: v for k, v in constructor_kwargs.items() if v is not None}
constructor_kwargs.update({"_escrow": escrow_address})
def _deploy_essential(self, contract_version: str, gas_limit: int = None, confirmations: int = 0) -> 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,
@ -823,7 +836,6 @@ class PolicyManagerDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
ignore_deployed: bool = False,
confirmations: int = 0,
emitter=None,
**overrides,
) -> Dict[str, dict]:
if deployment_mode not in (BARE, IDLE, FULL):
@ -836,8 +848,7 @@ class PolicyManagerDeployer(BaseContractDeployer, UpgradeableContractMixin, Owna
emitter.message(f"\nNext Transaction: {self.contract_name} Contract Creation", color='blue', bold=True)
policy_manager_contract, deploy_receipt = self._deploy_essential(contract_version=contract_version,
gas_limit=gas_limit,
confirmations=confirmations,
**overrides)
confirmations=confirmations)
# This is the end of bare deployment.
if deployment_mode is BARE:
@ -929,9 +940,15 @@ class StakingInterfaceDeployer(BaseContractDeployer, UpgradeableContractMixin, O
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,
contract_name=staking_contract_name,
proxy_name=staking_proxy_name)
try:
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=staking_contract_name,
proxy_name=staking_proxy_name)
except self.registry.UnknownContract:
staking_contract_name = StakingEscrowDeployer.contract_name_stub
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=staking_contract_name,
proxy_name=staking_proxy_name)
policy_contract_name = PolicyManagerDeployer.contract_name
policy_proxy_name = PolicyManagerDeployer._proxy_deployer.contract_name
@ -1140,16 +1157,17 @@ class AdjudicatorDeployer(BaseContractDeployer, UpgradeableContractMixin, Ownabl
contract_name=staking_contract_name,
proxy_name=proxy_name)
except self.registry.UnknownContract:
self.staking_contract = None
staking_contract_name = StakingEscrowDeployer.contract_name_stub
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=staking_contract_name,
proxy_name=proxy_name)
def check_deployment_readiness(self, *args, **kwargs) -> Tuple[bool, list]:
adjudicator_deployment_rules = []
if self.staking_contract is not None:
staking_escrow_owner = self.staking_contract.functions.owner().call()
adjudicator_deployment_rules.append(
(self.deployer_address == staking_escrow_owner,
f'{self.contract_name} must be deployed by the owner of {STAKING_ESCROW_CONTRACT_NAME} ({staking_escrow_owner})')
)
staking_escrow_owner = self.staking_contract.functions.owner().call()
adjudicator_deployment_rules = [
(self.deployer_address == staking_escrow_owner,
f'{self.contract_name} must be deployed by the owner of {STAKING_ESCROW_CONTRACT_NAME} ({staking_escrow_owner})')
]
return super().check_deployment_readiness(additional_rules=adjudicator_deployment_rules, *args, **kwargs)
def _deploy_essential(self, contract_version: str, gas_limit: int = None, confirmations: int = 0, **overrides):
@ -1164,8 +1182,7 @@ class AdjudicatorDeployer(BaseContractDeployer, UpgradeableContractMixin, Ownabl
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
escrow_address = self.staking_contract.address if self.staking_contract is not None else NULL_ADDRESS
constructor_kwargs.update({"_escrow": escrow_address})
constructor_kwargs.update({"_escrow": self.staking_contract.address})
adjudicator_contract, deploy_receipt = self.blockchain.deploy_contract(self.deployer_address,
self.registry,
self.contract_name,
@ -1249,11 +1266,26 @@ class WorklockDeployer(BaseContractDeployer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=self.registry) # type: NucypherTokenAgent
token_contract_name = NucypherTokenDeployer.contract_name
self.token_contract = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=token_contract_name)
staking_contract_name = StakingEscrowDeployer.contract_name
proxy_name = StakingEscrowDeployer._proxy_deployer.contract_name
try:
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=staking_contract_name,
proxy_name=proxy_name)
except self.registry.UnknownContract:
staking_contract_name = StakingEscrowDeployer.contract_name_stub
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
contract_name=staking_contract_name,
proxy_name=proxy_name)
def _deploy_essential(self, gas_limit: int = None, confirmations: int = 0):
# Deploy
constructor_args = (self.token_agent.contract_address,
constructor_args = (self.token_contract.address,
self.staking_contract.address,
*self.economics.worklock_deployment_parameters)
worklock_contract, receipt = self.blockchain.deploy_contract(self.deployer_address,
@ -1278,11 +1310,7 @@ class WorklockDeployer(BaseContractDeployer):
if deployment_mode != FULL:
raise self.ContractDeploymentError(f"{self.contract_name} cannot be deployed in {deployment_mode} mode")
# staking_escrow_rule = [
# (self.staking_agent.worklock == NULL_ADDRESS,
# f"StakingEscrow already has a WorkLock reference ({self.staking_agent.worklock})")
# ]
self.check_deployment_readiness(ignore_deployed=ignore_deployed)#, additional_rules=staking_escrow_rule)
self.check_deployment_readiness(ignore_deployed=ignore_deployed)
# Essential
if emitter:

View File

@ -13,7 +13,7 @@ import "zeppelin/math/Math.sol";
/**
* @title Adjudicator
* @notice Supervises stakers' behavior and punishes when something's wrong.
* @dev |v2.2.1|
* @dev |v2.1.2|
*/
contract Adjudicator is Upgradeable {
@ -35,6 +35,7 @@ contract Adjudicator is Upgradeable {
bytes32 constant RESERVED_CAPSULE_AND_CFRAG_BYTES = bytes32(0);
address constant RESERVED_ADDRESS = address(0);
StakingEscrow public immutable escrow;
SignatureVerifier.HashAlgorithm public immutable hashAlgorithm;
uint256 public immutable basePenalty;
uint256 public immutable penaltyHistoryCoefficient;
@ -43,10 +44,9 @@ contract Adjudicator is Upgradeable {
mapping (address => uint256) public penaltyHistory;
mapping (bytes32 => bool) public evaluatedCFrags;
StakingEscrow public escrow;
/**
* @param _escrow Escrow contract. Zero address if not yet deployed
* @param _escrow Escrow contract
* @param _hashAlgorithm Hashing algorithm
* @param _basePenalty Base for the penalty calculation
* @param _penaltyHistoryCoefficient Coefficient for calculating the penalty depending on the history
@ -62,8 +62,7 @@ contract Adjudicator is Upgradeable {
uint256 _rewardCoefficient
) {
// Sanity checks.
require(// This contract has an escrow if it's not the null address.
(address(_escrow) == address(0) || _escrow.secondsPerPeriod() > 0) &&
require(_escrow.secondsPerPeriod() > 0 && // This contract has an escrow, and it's not the null address.
// The reward and penalty coefficients are set.
_percentagePenaltyCoefficient != 0 &&
_rewardCoefficient != 0);
@ -75,17 +74,6 @@ contract Adjudicator is Upgradeable {
rewardCoefficient = _rewardCoefficient;
}
/**
* @notice Sets address of the escrow contract
* @param _escrow Escrow contract
*/
function setStakingEscrow(StakingEscrow _escrow) external onlyOwner {
// StakingEscrow can be set only once
require(address(escrow) == address(0));
require(_escrow.secondsPerPeriod() > 0); // This contract has an escrow, and it's not the null address.
escrow = _escrow;
}
/**
* @notice Submit proof that a worker created wrong CFrag
* @param _capsuleBytes Serialized capsule
@ -203,7 +191,6 @@ contract Adjudicator is Upgradeable {
/// @dev the `onlyWhileUpgrading` modifier works through a call to the parent `verifyState`
function verifyState(address _testTarget) public override virtual {
super.verifyState(_testTarget);
require(address(delegateGet(_testTarget, this.escrow.selector)) == address(escrow));
bytes32 evaluationCFragHash = SignatureVerifier.hash(
abi.encodePacked(RESERVED_CAPSULE_AND_CFRAG_BYTES), SignatureVerifier.HashAlgorithm.SHA256);
require(delegateGet(_testTarget, this.evaluatedCFrags.selector, evaluationCFragHash) ==
@ -215,10 +202,6 @@ contract Adjudicator is Upgradeable {
/// @dev the `onlyWhileUpgrading` modifier works through a call to the parent `finishUpgrade`
function finishUpgrade(address _target) public override virtual {
super.finishUpgrade(_target);
StakingEscrow escrowAddress = Adjudicator(_target).escrow();
if (address(escrowAddress) != address(0)) {
escrow = escrowAddress;
}
// preparation for the verifyState method
bytes32 evaluationCFragHash = SignatureVerifier.hash(
abi.encodePacked(RESERVED_CAPSULE_AND_CFRAG_BYTES), SignatureVerifier.HashAlgorithm.SHA256);

View File

@ -1,555 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.7.0;
import "zeppelin/math/SafeMath.sol";
import "zeppelin/token/ERC20/SafeERC20.sol";
import "zeppelin/utils/Address.sol";
import "zeppelin/ownership/Ownable.sol";
import "contracts/NuCypherToken.sol";
import "contracts/StakingEscrow.sol";
import "contracts/lib/AdditionalMath.sol";
/**
* @notice The WorkLock distribution contract
*/
contract WorkLock is Ownable {
using SafeERC20 for NuCypherToken;
using SafeMath for uint256;
using AdditionalMath for uint256;
using Address for address payable;
using Address for address;
event Deposited(address indexed sender, uint256 value);
event Bid(address indexed sender, uint256 depositedETH);
event Claimed(address indexed sender, uint256 claimedTokens);
event Refund(address indexed sender, uint256 refundETH, uint256 completedWork);
event Canceled(address indexed sender, uint256 value);
event BiddersChecked(address indexed sender, uint256 startIndex, uint256 endIndex);
event ForceRefund(address indexed sender, address indexed bidder, uint256 refundETH);
event CompensationWithdrawn(address indexed sender, uint256 value);
event Shutdown(address indexed sender);
struct WorkInfo {
uint256 depositedETH;
uint256 completedWork;
bool claimed;
uint128 index;
}
uint16 public constant SLOWING_REFUND = 100;
uint256 private constant MAX_ETH_SUPPLY = 2e10 ether;
NuCypherToken public immutable token;
StakingEscrow public immutable escrow;
/*
* @dev WorkLock calculations:
* bid = minBid + bonusETHPart
* bonusTokenSupply = tokenSupply - bidders.length * minAllowableLockedTokens
* bonusDepositRate = bonusTokenSupply / bonusETHSupply
* claimedTokens = minAllowableLockedTokens + bonusETHPart * bonusDepositRate
* bonusRefundRate = bonusDepositRate * SLOWING_REFUND / boostingRefund
* refundETH = completedWork / refundRate
*/
uint256 public immutable boostingRefund;
uint256 public immutable minAllowedBid;
uint16 public immutable stakingPeriods;
// copy from the escrow contract
uint256 public immutable maxAllowableLockedTokens;
uint256 public immutable minAllowableLockedTokens;
uint256 public tokenSupply;
uint256 public startBidDate;
uint256 public endBidDate;
uint256 public endCancellationDate;
uint256 public bonusETHSupply;
mapping(address => WorkInfo) public workInfo;
mapping(address => uint256) public compensation;
address[] public bidders;
// if value == bidders.length then WorkLock is fully checked
uint256 public nextBidderToCheck;
/**
* @dev Checks timestamp regarding cancellation window
*/
modifier afterCancellationWindow()
{
require(block.timestamp >= endCancellationDate,
"Operation is allowed when cancellation phase is over");
_;
}
/**
* @param _token Token contract
* @param _escrow Escrow contract
* @param _startBidDate Timestamp when bidding starts
* @param _endBidDate Timestamp when bidding will end
* @param _endCancellationDate Timestamp when cancellation will ends
* @param _boostingRefund Coefficient to boost refund ETH
* @param _stakingPeriods Amount of periods during which tokens will be locked after claiming
* @param _minAllowedBid Minimum allowed ETH amount for bidding
*/
constructor(
NuCypherToken _token,
StakingEscrow _escrow,
uint256 _startBidDate,
uint256 _endBidDate,
uint256 _endCancellationDate,
uint256 _boostingRefund,
uint16 _stakingPeriods,
uint256 _minAllowedBid
) {
uint256 totalSupply = _token.totalSupply();
require(totalSupply > 0 && // token contract is deployed and accessible
_escrow.secondsPerPeriod() > 0 && // escrow contract is deployed and accessible
_escrow.token() == _token && // same token address for worklock and escrow
_endBidDate > _startBidDate && // bidding period lasts some time
_endBidDate > block.timestamp && // there is time to make a bid
_endCancellationDate >= _endBidDate && // cancellation window includes bidding
_minAllowedBid > 0 && // min allowed bid was set
_boostingRefund > 0 && // boosting coefficient was set
_stakingPeriods >= _escrow.minLockedPeriods()); // staking duration is consistent with escrow contract
// worst case for `ethToWork()` and `workToETH()`,
// when ethSupply == MAX_ETH_SUPPLY and tokenSupply == totalSupply
require(MAX_ETH_SUPPLY * totalSupply * SLOWING_REFUND / MAX_ETH_SUPPLY / totalSupply == SLOWING_REFUND &&
MAX_ETH_SUPPLY * totalSupply * _boostingRefund / MAX_ETH_SUPPLY / totalSupply == _boostingRefund);
token = _token;
escrow = _escrow;
startBidDate = _startBidDate;
endBidDate = _endBidDate;
endCancellationDate = _endCancellationDate;
boostingRefund = _boostingRefund;
stakingPeriods = _stakingPeriods;
minAllowedBid = _minAllowedBid;
maxAllowableLockedTokens = _escrow.maxAllowableLockedTokens();
minAllowableLockedTokens = _escrow.minAllowableLockedTokens();
}
/**
* @notice Deposit tokens to contract
* @param _value Amount of tokens to transfer
*/
function tokenDeposit(uint256 _value) external {
require(block.timestamp < endBidDate, "Can't deposit more tokens after end of bidding");
token.safeTransferFrom(msg.sender, address(this), _value);
tokenSupply += _value;
emit Deposited(msg.sender, _value);
}
/**
* @notice Calculate amount of tokens that will be get for specified amount of ETH
* @dev This value will be fixed only after end of bidding
*/
function ethToTokens(uint256 _ethAmount) public view returns (uint256) {
if (_ethAmount < minAllowedBid) {
return 0;
}
// when all participants bid with the same minimum amount of eth
if (bonusETHSupply == 0) {
return tokenSupply / bidders.length;
}
uint256 bonusETH = _ethAmount - minAllowedBid;
uint256 bonusTokenSupply = tokenSupply - bidders.length * minAllowableLockedTokens;
return minAllowableLockedTokens + bonusETH.mul(bonusTokenSupply).div(bonusETHSupply);
}
/**
* @notice Calculate amount of work that need to be done to refund specified amount of ETH
*/
function ethToWork(uint256 _ethAmount, uint256 _tokenSupply, uint256 _ethSupply)
internal view returns (uint256)
{
return _ethAmount.mul(_tokenSupply).mul(SLOWING_REFUND).divCeil(_ethSupply.mul(boostingRefund));
}
/**
* @notice Calculate amount of work that need to be done to refund specified amount of ETH
* @dev This value will be fixed only after end of bidding
* @param _ethToReclaim Specified sum of ETH staker wishes to reclaim following completion of work
* @param _restOfDepositedETH Remaining ETH in staker's deposit once ethToReclaim sum has been subtracted
* @dev _ethToReclaim + _restOfDepositedETH = depositedETH
*/
function ethToWork(uint256 _ethToReclaim, uint256 _restOfDepositedETH) internal view returns (uint256) {
uint256 baseETHSupply = bidders.length * minAllowedBid;
// when all participants bid with the same minimum amount of eth
if (bonusETHSupply == 0) {
return ethToWork(_ethToReclaim, tokenSupply, baseETHSupply);
}
uint256 baseETH = 0;
uint256 bonusETH = 0;
// If the staker's total remaining deposit (including the specified sum of ETH to reclaim)
// is lower than the minimum bid size,
// then only the base part is used to calculate the work required to reclaim ETH
if (_ethToReclaim + _restOfDepositedETH <= minAllowedBid) {
baseETH = _ethToReclaim;
// If the staker's remaining deposit (not including the specified sum of ETH to reclaim)
// is still greater than the minimum bid size,
// then only the bonus part is used to calculate the work required to reclaim ETH
} else if (_restOfDepositedETH >= minAllowedBid) {
bonusETH = _ethToReclaim;
// If the staker's remaining deposit (not including the specified sum of ETH to reclaim)
// is lower than the minimum bid size,
// then both the base and bonus parts must be used to calculate the work required to reclaim ETH
} else {
bonusETH = _ethToReclaim + _restOfDepositedETH - minAllowedBid;
baseETH = _ethToReclaim - bonusETH;
}
uint256 baseTokenSupply = bidders.length * minAllowableLockedTokens;
uint256 work = 0;
if (baseETH > 0) {
work = ethToWork(baseETH, baseTokenSupply, baseETHSupply);
}
if (bonusETH > 0) {
uint256 bonusTokenSupply = tokenSupply - baseTokenSupply;
work += ethToWork(bonusETH, bonusTokenSupply, bonusETHSupply);
}
return work;
}
/**
* @notice Calculate amount of work that need to be done to refund specified amount of ETH
* @dev This value will be fixed only after end of bidding
*/
function ethToWork(uint256 _ethAmount) public view returns (uint256) {
return ethToWork(_ethAmount, 0);
}
/**
* @notice Calculate amount of ETH that will be refund for completing specified amount of work
*/
function workToETH(uint256 _completedWork, uint256 _ethSupply, uint256 _tokenSupply)
internal view returns (uint256)
{
return _completedWork.mul(_ethSupply).mul(boostingRefund).div(_tokenSupply.mul(SLOWING_REFUND));
}
/**
* @notice Calculate amount of ETH that will be refund for completing specified amount of work
* @dev This value will be fixed only after end of bidding
*/
function workToETH(uint256 _completedWork, uint256 _depositedETH) public view returns (uint256) {
uint256 baseETHSupply = bidders.length * minAllowedBid;
// when all participants bid with the same minimum amount of eth
if (bonusETHSupply == 0) {
return workToETH(_completedWork, baseETHSupply, tokenSupply);
}
uint256 bonusWork = 0;
uint256 bonusETH = 0;
uint256 baseTokenSupply = bidders.length * minAllowableLockedTokens;
if (_depositedETH > minAllowedBid) {
bonusETH = _depositedETH - minAllowedBid;
uint256 bonusTokenSupply = tokenSupply - baseTokenSupply;
bonusWork = ethToWork(bonusETH, bonusTokenSupply, bonusETHSupply);
if (_completedWork <= bonusWork) {
return workToETH(_completedWork, bonusETHSupply, bonusTokenSupply);
}
}
_completedWork -= bonusWork;
return bonusETH + workToETH(_completedWork, baseETHSupply, baseTokenSupply);
}
/**
* @notice Get remaining work to full refund
*/
function getRemainingWork(address _bidder) external view returns (uint256) {
WorkInfo storage info = workInfo[_bidder];
uint256 completedWork = escrow.getCompletedWork(_bidder).sub(info.completedWork);
uint256 remainingWork = ethToWork(info.depositedETH);
if (remainingWork <= completedWork) {
return 0;
}
return remainingWork - completedWork;
}
/**
* @notice Get length of bidders array
*/
function getBiddersLength() external view returns (uint256) {
return bidders.length;
}
/**
* @notice Bid for tokens by transferring ETH
*/
function bid() external payable {
require(block.timestamp >= startBidDate, "Bidding is not open yet");
require(block.timestamp < endBidDate, "Bidding is already finished");
WorkInfo storage info = workInfo[msg.sender];
// first bid
if (info.depositedETH == 0) {
require(msg.value >= minAllowedBid, "Bid must be at least minimum");
require(bidders.length < tokenSupply / minAllowableLockedTokens, "Not enough tokens for more bidders");
info.index = uint128(bidders.length);
bidders.push(msg.sender);
bonusETHSupply = bonusETHSupply.add(msg.value - minAllowedBid);
} else {
bonusETHSupply = bonusETHSupply.add(msg.value);
}
info.depositedETH = info.depositedETH.add(msg.value);
emit Bid(msg.sender, msg.value);
}
/**
* @notice Cancel bid and refund deposited ETH
*/
function cancelBid() external {
require(block.timestamp < endCancellationDate,
"Cancellation allowed only during cancellation window");
WorkInfo storage info = workInfo[msg.sender];
require(info.depositedETH > 0, "No bid to cancel");
require(!info.claimed, "Tokens are already claimed");
uint256 refundETH = info.depositedETH;
info.depositedETH = 0;
// remove from bidders array, move last bidder to the empty place
uint256 lastIndex = bidders.length - 1;
if (info.index != lastIndex) {
address lastBidder = bidders[lastIndex];
bidders[info.index] = lastBidder;
workInfo[lastBidder].index = info.index;
}
bidders.pop();
if (refundETH > minAllowedBid) {
bonusETHSupply = bonusETHSupply.sub(refundETH - minAllowedBid);
}
msg.sender.sendValue(refundETH);
emit Canceled(msg.sender, refundETH);
}
/**
* @notice Cancels distribution, makes possible to retrieve all bids and owner gets all tokens
*/
function shutdown() external onlyOwner {
require(!isClaimingAvailable(), "Claiming has already been enabled");
internalShutdown();
}
/**
* @notice Cancels distribution, makes possible to retrieve all bids and owner gets all tokens
*/
function internalShutdown() internal {
startBidDate = 0;
endBidDate = 0;
endCancellationDate = uint256(0) - 1; // "infinite" cancellation window
token.safeTransfer(owner(), tokenSupply);
emit Shutdown(msg.sender);
}
/**
* @notice Make force refund to bidders who can get tokens more than maximum allowed
* @param _biddersForRefund Sorted list of unique bidders. Only bidders who must receive a refund
*/
function forceRefund(address payable[] calldata _biddersForRefund) external afterCancellationWindow {
require(nextBidderToCheck != bidders.length, "Bidders have already been checked");
uint256 length = _biddersForRefund.length;
require(length > 0, "Must be at least one bidder for a refund");
uint256 minNumberOfBidders = tokenSupply.divCeil(maxAllowableLockedTokens);
if (bidders.length < minNumberOfBidders) {
internalShutdown();
return;
}
address previousBidder = _biddersForRefund[0];
uint256 minBid = workInfo[previousBidder].depositedETH;
uint256 maxBid = minBid;
// get minimum and maximum bids
for (uint256 i = 1; i < length; i++) {
address bidder = _biddersForRefund[i];
uint256 depositedETH = workInfo[bidder].depositedETH;
require(bidder > previousBidder && depositedETH > 0, "Addresses must be an array of unique bidders");
if (minBid > depositedETH) {
minBid = depositedETH;
} else if (maxBid < depositedETH) {
maxBid = depositedETH;
}
previousBidder = bidder;
}
uint256[] memory refunds = new uint256[](length);
// first step - align at a minimum bid
if (minBid != maxBid) {
for (uint256 i = 0; i < length; i++) {
address bidder = _biddersForRefund[i];
WorkInfo storage info = workInfo[bidder];
if (info.depositedETH > minBid) {
refunds[i] = info.depositedETH - minBid;
info.depositedETH = minBid;
bonusETHSupply -= refunds[i];
}
}
}
require(ethToTokens(minBid) > maxAllowableLockedTokens,
"At least one of bidders has allowable bid");
// final bids adjustment (only for bonus part)
// (min_whale_bid * token_supply - max_stake * eth_supply) / (token_supply - max_stake * n_whales)
uint256 maxBonusTokens = maxAllowableLockedTokens - minAllowableLockedTokens;
uint256 minBonusETH = minBid - minAllowedBid;
uint256 bonusTokenSupply = tokenSupply - bidders.length * minAllowableLockedTokens;
uint256 refundETH = minBonusETH.mul(bonusTokenSupply)
.sub(maxBonusTokens.mul(bonusETHSupply))
.divCeil(bonusTokenSupply - maxBonusTokens.mul(length));
uint256 resultBid = minBid.sub(refundETH);
bonusETHSupply -= length * refundETH;
for (uint256 i = 0; i < length; i++) {
address bidder = _biddersForRefund[i];
WorkInfo storage info = workInfo[bidder];
refunds[i] += refundETH;
info.depositedETH = resultBid;
}
// reset verification
nextBidderToCheck = 0;
// save a refund
for (uint256 i = 0; i < length; i++) {
address bidder = _biddersForRefund[i];
compensation[bidder] += refunds[i];
emit ForceRefund(msg.sender, bidder, refunds[i]);
}
}
/**
* @notice Withdraw compensation after force refund
*/
function withdrawCompensation() external {
uint256 refund = compensation[msg.sender];
require(refund > 0, "There is no compensation");
compensation[msg.sender] = 0;
msg.sender.sendValue(refund);
emit CompensationWithdrawn(msg.sender, refund);
}
/**
* @notice Check that the claimed tokens are within `maxAllowableLockedTokens` for all participants,
* starting from the last point `nextBidderToCheck`
* @dev Method stops working when the remaining gas is less than `_gasToSaveState`
* and saves the state in `nextBidderToCheck`.
* If all bidders have been checked then `nextBidderToCheck` will be equal to the length of the bidders array
*/
function verifyBiddingCorrectness(uint256 _gasToSaveState) external afterCancellationWindow returns (uint256) {
require(nextBidderToCheck != bidders.length, "Bidders have already been checked");
// all participants bid with the same minimum amount of eth
uint256 index = nextBidderToCheck;
if (bonusETHSupply == 0) {
require(tokenSupply / bidders.length <= maxAllowableLockedTokens, "Not enough bidders");
index = bidders.length;
}
uint256 maxBonusTokens = maxAllowableLockedTokens - minAllowableLockedTokens;
uint256 bonusTokenSupply = tokenSupply - bidders.length * minAllowableLockedTokens;
uint256 maxBidFromMaxStake = minAllowedBid + maxBonusTokens.mul(bonusETHSupply).div(bonusTokenSupply);
while (index < bidders.length && gasleft() > _gasToSaveState) {
address bidder = bidders[index];
require(workInfo[bidder].depositedETH <= maxBidFromMaxStake, "Bid is greater than max allowable bid");
index++;
}
if (index != nextBidderToCheck) {
emit BiddersChecked(msg.sender, nextBidderToCheck, index);
nextBidderToCheck = index;
}
return nextBidderToCheck;
}
/**
* @notice Checks if claiming available
*/
function isClaimingAvailable() public view returns (bool) {
return block.timestamp >= endCancellationDate &&
nextBidderToCheck == bidders.length;
}
/**
* @notice Claimed tokens will be deposited and locked as stake in the StakingEscrow contract.
*/
function claim() external returns (uint256 claimedTokens) {
require(isClaimingAvailable(), "Claiming has not been enabled yet");
WorkInfo storage info = workInfo[msg.sender];
require(!info.claimed, "Tokens are already claimed");
claimedTokens = ethToTokens(info.depositedETH);
require(claimedTokens > 0, "Nothing to claim");
info.claimed = true;
token.approve(address(escrow), claimedTokens);
escrow.depositFromWorkLock(msg.sender, claimedTokens, stakingPeriods);
info.completedWork = escrow.setWorkMeasurement(msg.sender, true);
emit Claimed(msg.sender, claimedTokens);
}
/**
* @notice Get available refund for bidder
*/
function getAvailableRefund(address _bidder) public view returns (uint256) {
WorkInfo storage info = workInfo[_bidder];
// nothing to refund
if (info.depositedETH == 0) {
return 0;
}
uint256 currentWork = escrow.getCompletedWork(_bidder);
uint256 completedWork = currentWork.sub(info.completedWork);
// no work that has been completed since last refund
if (completedWork == 0) {
return 0;
}
uint256 refundETH = workToETH(completedWork, info.depositedETH);
if (refundETH > info.depositedETH) {
refundETH = info.depositedETH;
}
return refundETH;
}
/**
* @notice Refund ETH for the completed work
*/
function refund() external returns (uint256 refundETH) {
WorkInfo storage info = workInfo[msg.sender];
require(info.claimed, "Tokens must be claimed before refund");
refundETH = getAvailableRefund(msg.sender);
require(refundETH > 0, "Nothing to refund: there is no ETH to refund or no completed work");
if (refundETH == info.depositedETH) {
escrow.setWorkMeasurement(msg.sender, false);
}
info.depositedETH = info.depositedETH.sub(refundETH);
// convert refund back to work to eliminate potential rounding errors
uint256 completedWork = ethToWork(refundETH, info.depositedETH);
info.completedWork = info.completedWork.add(completedWork);
emit Refund(msg.sender, refundETH, completedWork);
msg.sender.sendValue(refundETH);
}
}

View File

@ -17,7 +17,7 @@ import "contracts/proxy/Upgradeable.sol";
/**
* @title PolicyManager
* @notice Contract holds policy data and locks accrued policy fees
* @dev |v6.2.1|
* @dev |v6.1.3|
*/
contract PolicyManager is Upgradeable {
using SafeERC20 for NuCypherToken;
@ -108,37 +108,25 @@ contract PolicyManager is Upgradeable {
// controlled overflow to get max int256
int256 public constant DEFAULT_FEE_DELTA = int256((uint256(0) - 1) >> 1);
StakingEscrow public immutable escrow;
uint32 public immutable secondsPerPeriod;
mapping (bytes16 => Policy) public policies;
mapping (address => NodeInfo) public nodes;
Range public feeRateRange;
StakingEscrow public escrow;
/**
* @notice Constructor sets either address of the escrow contract or seconds per period directly
* @notice Constructor sets address of the escrow contract
* @param _escrow Escrow contract
* @param _secondsPerPeriod Seconds per period from StakingEscrow contract
*/
constructor(StakingEscrow _escrow, uint32 _secondsPerPeriod) {
uint32 localSecondsPerPeriod = address(_escrow) != address(0) ? _escrow.secondsPerPeriod() : _secondsPerPeriod;
constructor(StakingEscrow _escrow) {
// if the input address is not the StakingEscrow then calling `secondsPerPeriod` will throw error
uint32 localSecondsPerPeriod = _escrow.secondsPerPeriod();
require(localSecondsPerPeriod > 0);
secondsPerPeriod = localSecondsPerPeriod;
escrow = _escrow;
}
/**
* @notice Sets address of the escrow contract
* @param _escrow Escrow contract
*/
function setStakingEscrow(StakingEscrow _escrow) external onlyOwner {
// StakingEscrow can be set only once
require(address(escrow) == address(0));
// if the input address is not the StakingEscrow then calling `secondsPerPeriod` will throw error
require(secondsPerPeriod == _escrow.secondsPerPeriod());
escrow = _escrow;
}
/**
* @dev Checks that sender is the StakingEscrow contract
*/
@ -731,7 +719,6 @@ contract PolicyManager is Upgradeable {
/// @dev the `onlyWhileUpgrading` modifier works through a call to the parent `verifyState`
function verifyState(address _testTarget) public override virtual {
super.verifyState(_testTarget);
require(address(delegateGet(_testTarget, this.escrow.selector)) == address(escrow));
Range memory rangeToCheck = delegateGetFeeRateRange(_testTarget);
require(feeRateRange.min == rangeToCheck.min &&
feeRateRange.defaultValue == rangeToCheck.defaultValue &&
@ -770,10 +757,6 @@ contract PolicyManager is Upgradeable {
/// @dev the `onlyWhileUpgrading` modifier works through a call to the parent `finishUpgrade`
function finishUpgrade(address _target) public override virtual {
super.finishUpgrade(_target);
StakingEscrow escrowAddress = PolicyManager(_target).escrow();
if (address(escrowAddress) != address(0)) {
escrow = escrowAddress;
}
// Create fake Policy and NodeInfo to use them in verifyState(address)
Policy storage policy = policies[RESERVED_POLICY_ID];
policy.sponsor = msg.sender;

View File

@ -41,6 +41,58 @@ interface WorkLockInterface {
function token() external view returns (NuCypherToken);
}
/**
* @title StakingEscrowStub
* @notice Stub is used to deploy main StakingEscrow after all other contract and make some variables immutable
*/
contract StakingEscrowStub is Upgradeable {
using AdditionalMath for uint32;
NuCypherToken public immutable token;
uint32 public immutable secondsPerPeriod;
uint16 public immutable minLockedPeriods;
uint256 public immutable minAllowableLockedTokens;
uint256 public immutable maxAllowableLockedTokens;
/**
* @notice Predefines some variables for use when deploying other contracts
* @param _token Token contract
* @param _minLockedPeriods Min amount of periods during which tokens can be locked
* @param _minAllowableLockedTokens Min amount of tokens that can be locked
* @param _maxAllowableLockedTokens Max amount of tokens that can be locked
*/
constructor(
NuCypherToken _token,
uint32 _hoursPerPeriod,
uint16 _minLockedPeriods,
uint256 _minAllowableLockedTokens,
uint256 _maxAllowableLockedTokens
) {
require(_token.totalSupply() > 0 &&
_hoursPerPeriod != 0 &&
_minLockedPeriods > 1 &&
_maxAllowableLockedTokens != 0);
token = _token;
secondsPerPeriod = _hoursPerPeriod.mul32(1 hours);
minLockedPeriods = _minLockedPeriods;
minAllowableLockedTokens = _minAllowableLockedTokens;
maxAllowableLockedTokens = _maxAllowableLockedTokens;
}
/// @dev the `onlyWhileUpgrading` modifier works through a call to the parent `verifyState`
function verifyState(address _testTarget) public override virtual {
super.verifyState(_testTarget);
// we have to use real values even though this is a stub
require(address(delegateGet(_testTarget, this.token.selector)) == address(token));
require(uint32(delegateGet(_testTarget, this.secondsPerPeriod.selector)) == secondsPerPeriod);
require(uint16(delegateGet(_testTarget, this.minLockedPeriods.selector)) == minLockedPeriods);
require(delegateGet(_testTarget, this.minAllowableLockedTokens.selector) == minAllowableLockedTokens);
require(delegateGet(_testTarget, this.maxAllowableLockedTokens.selector) == maxAllowableLockedTokens);
}
}
/**
* @title StakingEscrow
@ -156,6 +208,9 @@ contract StakingEscrow is Issuer, IERC900History {
/**
* @notice Constructor sets address of token contract and coefficients for minting
* @param _token Token contract
* @param _policyManager Policy Manager contract
* @param _adjudicator Adjudicator contract
* @param _workLock WorkLock contract. Zero address if there is no WorkLock
* @param _hoursPerPeriod Size of period in hours
* @param _issuanceDecayCoefficient (d) Coefficient which modifies the rate at which the maximum issuance decays,
* only applicable to Phase 2. d = 365 * half-life / LOG2 where default half-life = 2.
@ -179,12 +234,12 @@ contract StakingEscrow is Issuer, IERC900History {
* @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
* @param _policyManager Policy Manager contract
* @param _adjudicator Adjudicator contract
* @param _workLock WorkLock contract. Zero address if there is no WorkLock
*/
constructor(
NuCypherToken _token,
PolicyManagerInterface _policyManager,
AdjudicatorInterface _adjudicator,
WorkLockInterface _workLock,
uint32 _hoursPerPeriod,
uint256 _issuanceDecayCoefficient,
uint256 _lockDurationCoefficient1,
@ -195,10 +250,7 @@ contract StakingEscrow is Issuer, IERC900History {
uint16 _minLockedPeriods,
uint256 _minAllowableLockedTokens,
uint256 _maxAllowableLockedTokens,
uint16 _minWorkerPeriods,
PolicyManagerInterface _policyManager,
AdjudicatorInterface _adjudicator,
WorkLockInterface _workLock
uint16 _minWorkerPeriods
)
Issuer(
_token,
@ -236,32 +288,6 @@ contract StakingEscrow is Issuer, IERC900History {
_;
}
//------------------------Initialization------------------------
/**
* @notice Set policy manager, worklock and adjudicator addresses
*/
// function setContracts(
// PolicyManagerInterface _policyManager,
// AdjudicatorInterface _adjudicator,
// WorkLockInterface _workLock
// )
// external onlyOwner
// {
// // Policy manager can be set only once
// require(address(policyManager) == address(0) &&
// address(adjudicator) == address(0) &&
// address(workLock) == address(0)
// );
// // This escrow must be the escrow for the new policy manager
// require(_policyManager.escrow() == address(this) &&
// _adjudicator.escrow() == address(this) &&
// (address(_workLock) == address(0) || _workLock.escrow() == address(this))
// );
// policyManager = _policyManager;
// adjudicator = _adjudicator;
// workLock = _workLock;
// }
//------------------------Main getters------------------------
/**
* @notice Get all tokens belonging to the staker

View File

@ -43,6 +43,7 @@ contract WorkLock is Ownable {
uint256 private constant MAX_ETH_SUPPLY = 2e10 ether;
NuCypherToken public immutable token;
StakingEscrow public immutable escrow;
/*
* @dev WorkLock calculations:
@ -56,6 +57,9 @@ contract WorkLock is Ownable {
uint256 public immutable boostingRefund;
uint256 public immutable minAllowedBid;
uint16 public immutable stakingPeriods;
// copy from the escrow contract
uint256 public immutable maxAllowableLockedTokens;
uint256 public immutable minAllowableLockedTokens;
uint256 public tokenSupply;
uint256 public startBidDate;
@ -70,11 +74,6 @@ contract WorkLock is Ownable {
// if value == bidders.length then WorkLock is fully checked
uint256 public nextBidderToCheck;
StakingEscrow public escrow;
// copy from the escrow contract
uint256 public maxAllowableLockedTokens;
uint256 public minAllowableLockedTokens;
/**
* @dev Checks timestamp regarding cancellation window
*/
@ -87,6 +86,7 @@ contract WorkLock is Ownable {
/**
* @param _token Token contract
* @param _escrow Escrow contract
* @param _startBidDate Timestamp when bidding starts
* @param _endBidDate Timestamp when bidding will end
* @param _endCancellationDate Timestamp when cancellation will ends
@ -96,6 +96,7 @@ contract WorkLock is Ownable {
*/
constructor(
NuCypherToken _token,
StakingEscrow _escrow,
uint256 _startBidDate,
uint256 _endBidDate,
uint256 _endCancellationDate,
@ -105,38 +106,27 @@ contract WorkLock is Ownable {
) {
uint256 totalSupply = _token.totalSupply();
require(totalSupply > 0 && // token contract is deployed and accessible
_stakingPeriods > 0 && // staking periods was set
_escrow.secondsPerPeriod() > 0 && // escrow contract is deployed and accessible
_escrow.token() == _token && // same token address for worklock and escrow
_endBidDate > _startBidDate && // bidding period lasts some time
_endBidDate > block.timestamp && // there is time to make a bid
_endCancellationDate >= _endBidDate && // cancellation window includes bidding
_minAllowedBid > 0 && // min allowed bid was set
_boostingRefund > 0); // boosting coefficient was set
_boostingRefund > 0 && // boosting coefficient was set
_stakingPeriods >= _escrow.minLockedPeriods()); // staking duration is consistent with escrow contract
// worst case for `ethToWork()` and `workToETH()`,
// when ethSupply == MAX_ETH_SUPPLY and tokenSupply == totalSupply
require(MAX_ETH_SUPPLY * totalSupply * SLOWING_REFUND / MAX_ETH_SUPPLY / totalSupply == SLOWING_REFUND &&
MAX_ETH_SUPPLY * totalSupply * _boostingRefund / MAX_ETH_SUPPLY / totalSupply == _boostingRefund);
token = _token;
escrow = _escrow;
startBidDate = _startBidDate;
endBidDate = _endBidDate;
endCancellationDate = _endCancellationDate;
boostingRefund = _boostingRefund;
stakingPeriods = _stakingPeriods;
minAllowedBid = _minAllowedBid;
}
/**
* @notice Sets address of the escrow contract
* @param _escrow Escrow contract
*/
function setStakingEscrow(StakingEscrow _escrow) external onlyOwner {
// StakingEscrow can be set only once
require(address(escrow) == address(0));
require(_escrow.secondsPerPeriod() > 0 && // escrow contract is deployed and accessible
_escrow.token() == token && // same token address for worklock and escrow
stakingPeriods >= _escrow.minLockedPeriods() // staking duration is consistent with escrow contract
);
escrow = _escrow;
maxAllowableLockedTokens = _escrow.maxAllowableLockedTokens();
minAllowableLockedTokens = _escrow.minAllowableLockedTokens();
}

View File

@ -454,8 +454,9 @@ def rollback(general_config, actor_options):
@option_confirmations
@click.option('--mode',
help="Deploy a contract following all steps ('full'), up to idle status ('idle'), "
"just initialization step ('init', only for StakingEscrow) "
"or just the bare contract ('bare'). Defaults to 'full'",
type=click.Choice(['full', 'idle', 'bare'], case_sensitive=False),
type=click.Choice(['full', 'idle', 'bare', 'init'], case_sensitive=False),
default='full'
)
@click.option('--activate', help="Activate a contract that is in idle mode", is_flag=True)

View File

@ -18,7 +18,8 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import pytest
from nucypher.blockchain.eth.deployers import (AdjudicatorDeployer, NucypherTokenDeployer, PolicyManagerDeployer,
StakingEscrowDeployer, StakingInterfaceDeployer, WorklockDeployer)
StakingEscrowDeployer, StakingInterfaceDeployer)
from constant_sorrow.constants import (FULL, INIT)
@pytest.fixture(scope="module")
@ -29,20 +30,16 @@ def token_deployer(testerchain, test_registry):
@pytest.fixture(scope="module")
def worklock_deployer(token_deployer,
testerchain,
test_registry,
token_economics):
def staking_escrow_stub_deployer(testerchain, token_deployer, test_registry):
token_deployer.deploy()
worklock_deployer = WorklockDeployer(registry=test_registry,
economics=token_economics,
deployer_address=testerchain.etherbase_account)
return worklock_deployer
staking_escrow_deployer = StakingEscrowDeployer(registry=test_registry,
deployer_address=testerchain.etherbase_account)
return staking_escrow_deployer
@pytest.fixture(scope="module")
def policy_manager_deployer(worklock_deployer, testerchain, test_registry):
worklock_deployer.deploy()
def policy_manager_deployer(staking_escrow_stub_deployer, testerchain, test_registry):
staking_escrow_stub_deployer.deploy(deployment_mode=INIT)
policy_manager_deployer = PolicyManagerDeployer(registry=test_registry,
deployer_address=testerchain.etherbase_account)
return policy_manager_deployer
@ -66,7 +63,6 @@ def staking_escrow_deployer(testerchain, adjudicator_deployer, test_registry):
@pytest.fixture(scope="module")
def staking_interface_deployer(staking_escrow_deployer, testerchain, test_registry):
staking_escrow_deployer.deploy()
staking_interface_deployer = StakingInterfaceDeployer(registry=test_registry,
deployer_address=testerchain.etherbase_account)
return staking_interface_deployer

View File

@ -35,6 +35,11 @@ def test_adjudicator_deployer(testerchain,
token_deployer = NucypherTokenDeployer(deployer_address=origin, registry=test_registry)
token_deployer.deploy()
staking_escrow_deployer = StakingEscrowDeployer(deployer_address=origin, registry=test_registry)
staking_escrow_deployer.deploy()
staking_agent = staking_escrow_deployer.make_agent() # 2 Staker Escrow
deployer = AdjudicatorDeployer(deployer_address=origin, registry=test_registry)
deployment_receipts = deployer.deploy(progress=deployment_progress)
@ -48,6 +53,8 @@ def test_adjudicator_deployer(testerchain,
adjudicator_agent = deployer.make_agent()
# Check default Adjudicator deployment parameters
assert staking_escrow_deployer.deployer_address != staking_agent.contract_address
assert adjudicator_agent.staking_escrow_contract == staking_agent.contract_address
assert adjudicator_agent.hash_algorithm == token_economics.hash_algorithm
assert adjudicator_agent.base_penalty == token_economics.base_penalty
assert adjudicator_agent.penalty_history_coefficient == token_economics.penalty_history_coefficient

View File

@ -50,6 +50,20 @@ def test_deploy_idle_network(testerchain, deployment_progress, test_registry):
another_token_agent = token_deployer.make_agent()
assert another_token_agent.contract_address == token_deployer.contract_address == token_agent.contract_address
#
# StakingEscrow - in INIT mode, i.e. stub for StakingEscrow
#
staking_escrow_deployer = StakingEscrowDeployer(registry=test_registry, deployer_address=origin)
assert staking_escrow_deployer.deployer_address == origin
with pytest.raises(BaseContractDeployer.ContractDeploymentError):
assert staking_escrow_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED
assert not staking_escrow_deployer.is_deployed()
staking_escrow_deployer.deploy(progress=deployment_progress,
deployment_mode=constants.INIT)
assert not staking_escrow_deployer.is_deployed()
#
# Policy Manager
#

View File

@ -51,6 +51,23 @@ def test_deploy_ethereum_contracts(testerchain,
assert len(another_token_agent.contract_address) == 42
assert another_token_agent.contract_address == token_deployer.contract_address == token_agent.contract_address
#
# StakingEscrowStub
#
staking_escrow_deployer = StakingEscrowDeployer(
registry=test_registry,
deployer_address=origin)
assert staking_escrow_deployer.deployer_address == origin
with pytest.raises(BaseContractDeployer.ContractDeploymentError):
assert staking_escrow_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED
assert not staking_escrow_deployer.is_deployed()
staking_escrow_deployer.deploy(progress=deployment_progress)
assert not staking_escrow_deployer.is_deployed()
assert len(staking_escrow_deployer.contract_address) == 42
#
# Policy Manager
#
@ -76,6 +93,7 @@ def test_deploy_ethereum_contracts(testerchain,
assert len(another_policy_agent.contract_address) == 42
assert another_policy_agent.contract_address == policy_manager_deployer.contract_address == policy_agent.contract_address
#
# Adjudicator
#
@ -101,12 +119,6 @@ def test_deploy_ethereum_contracts(testerchain,
assert len(another_adjudicator_agent.contract_address) == 42
assert another_adjudicator_agent.contract_address == adjudicator_deployer.contract_address == adjudicator_agent.contract_address
# overall deployment steps must match aggregated individual expected number of steps
all_deployment_transactions = token_deployer.deployment_steps + policy_manager_deployer.deployment_steps + \
adjudicator_deployer.deployment_steps
assert deployment_progress.num_steps == len(all_deployment_transactions)
#
# StakingEscrow
#
staking_escrow_deployer = StakingEscrowDeployer(
@ -118,7 +130,7 @@ def test_deploy_ethereum_contracts(testerchain,
assert staking_escrow_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED
assert not staking_escrow_deployer.is_deployed()
staking_escrow_deployer.deploy(progress=deployment_progress)
staking_escrow_deployer.deploy(progress=deployment_progress, deployment_mode=constants.FULL)
assert staking_escrow_deployer.is_deployed()
assert len(staking_escrow_deployer.contract_address) == 42
@ -129,3 +141,9 @@ def test_deploy_ethereum_contracts(testerchain,
another_staking_agent = staking_escrow_deployer.make_agent()
assert len(another_staking_agent.contract_address) == 42
assert another_staking_agent.contract_address == staking_escrow_deployer.contract_address == staking_agent.contract_address
# overall deployment steps must match aggregated individual expected number of steps
all_deployment_transactions = token_deployer.deployment_steps + staking_escrow_deployer.init_steps + \
staking_escrow_deployer.deployment_steps + \
policy_manager_deployer.deployment_steps + adjudicator_deployer.deployment_steps
assert deployment_progress.num_steps == len(all_deployment_transactions) #

View File

@ -14,13 +14,14 @@ 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/>.
"""
from constant_sorrow import constants
from nucypher.blockchain.eth.agents import ContractAgency, PolicyManagerAgent, StakingEscrowAgent
from nucypher.blockchain.eth.constants import POLICY_MANAGER_CONTRACT_NAME
from nucypher.blockchain.eth.deployers import (DispatcherDeployer, PolicyManagerDeployer)
def test_policy_manager_deployment(policy_manager_deployer, deployment_progress):
def test_policy_manager_deployment(policy_manager_deployer, staking_escrow_stub_deployer, deployment_progress):
assert policy_manager_deployer.contract_name == POLICY_MANAGER_CONTRACT_NAME
@ -33,6 +34,9 @@ def test_policy_manager_deployment(policy_manager_deployer, deployment_progress)
for step_title in steps:
assert deployment_receipts[step_title]['status'] == 1
staking_escrow_address = policy_manager_deployer.contract.functions.escrow().call()
assert staking_escrow_stub_deployer.contract_address == staking_escrow_address
def test_make_agent(policy_manager_deployer, test_registry):
@ -47,11 +51,6 @@ def test_make_agent(policy_manager_deployer, test_registry):
assert policy_agent.contract_address == some_policy_agent.contract_address
def test_deployment_parameters(policy_manager_deployer, token_economics):
seconds_per_period = token_economics.seconds_per_period
assert seconds_per_period == policy_manager_deployer.contract.functions.secondsPerPeriod().call()
def test_policy_manager_has_dispatcher(policy_manager_deployer, testerchain, test_registry):
# Let's get the "bare" PolicyManager contract (i.e., unwrapped, no dispatcher)

View File

@ -17,6 +17,7 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import pytest
from constant_sorrow import constants
from nucypher.blockchain.eth.deployers import (AdjudicatorDeployer, NucypherTokenDeployer, PolicyManagerDeployer,
PreallocationEscrowDeployer, StakingEscrowDeployer,
@ -37,6 +38,9 @@ def test_staking_interface_deployer(testerchain, deployment_progress, test_regis
token_deployer = NucypherTokenDeployer(deployer_address=origin, registry=test_registry)
token_deployer.deploy()
staking_escrow_deployer = StakingEscrowDeployer(deployer_address=origin, registry=test_registry)
staking_escrow_deployer.deploy()
policy_manager_deployer = PolicyManagerDeployer(deployer_address=origin, registry=test_registry)
policy_manager_deployer.deploy()
@ -44,7 +48,7 @@ def test_staking_interface_deployer(testerchain, deployment_progress, test_regis
adjudicator_deployer.deploy()
staking_escrow_deployer = StakingEscrowDeployer(deployer_address=origin, registry=test_registry)
staking_escrow_deployer.deploy()
staking_escrow_deployer.deploy(deployment_mode=constants.FULL)
#
# Test

View File

@ -14,6 +14,7 @@ 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/>.
"""
from constant_sorrow import constants
from constant_sorrow.constants import BARE
@ -22,10 +23,10 @@ from nucypher.blockchain.eth.deployers import (DispatcherDeployer, StakingEscrow
def test_staking_escrow_deployment(staking_escrow_deployer, deployment_progress):
deployment_receipts = staking_escrow_deployer.deploy(progress=deployment_progress)
deployment_receipts = staking_escrow_deployer.deploy(progress=deployment_progress, deployment_mode=constants.FULL)
# deployment steps must match expected number of steps
assert deployment_progress.num_steps == len(staking_escrow_deployer.deployment_steps) == len(deployment_receipts) == 7
assert deployment_progress.num_steps == len(staking_escrow_deployer.deployment_steps) == len(deployment_receipts) == 4
for step in staking_escrow_deployer.deployment_steps:
assert deployment_receipts[step]['status'] == 1

View File

@ -24,7 +24,25 @@ from nucypher.blockchain.eth.constants import WORKLOCK_CONTRACT_NAME
from nucypher.blockchain.eth.deployers import WorklockDeployer
@pytest.fixture(scope='module')
def baseline_deployment(adjudicator_deployer):
adjudicator_deployer.deploy()
@pytest.fixture(scope="module")
def worklock_deployer(baseline_deployment,
testerchain,
test_registry,
token_economics):
worklock_deployer = WorklockDeployer(registry=test_registry,
economics=token_economics,
deployer_address=testerchain.etherbase_account)
return worklock_deployer
def test_worklock_deployment(worklock_deployer,
baseline_deployment,
staking_escrow_stub_deployer,
deployment_progress,
test_registry,
testerchain):
@ -41,6 +59,10 @@ def test_worklock_deployment(worklock_deployer,
for step_title in steps:
assert deployment_receipts[step_title]['status'] == 1
# Ensure the correct staking escrow address is set
staking_escrow_address = worklock_deployer.contract.functions.escrow().call()
assert staking_escrow_stub_deployer.contract_address == staking_escrow_address
def test_make_agent(worklock_deployer, test_registry):

View File

@ -93,7 +93,7 @@ def test_upgrade_contracts(click_runner, test_registry_source_manager, test_regi
#
# Check the existing state of the registry before the meat and potatoes
expected_enrollments = 10
expected_enrollments = 11
with open(registry_filepath, 'r') as file:
raw_registry_data = file.read()
registry_data = json.loads(raw_registry_data)

View File

@ -30,7 +30,7 @@ from nucypher.blockchain.eth.constants import (
DISPATCHER_CONTRACT_NAME,
NUCYPHER_TOKEN_CONTRACT_NAME,
POLICY_MANAGER_CONTRACT_NAME,
STAKING_ESCROW_CONTRACT_NAME
STAKING_ESCROW_CONTRACT_NAME, STAKING_ESCROW_STUB_CONTRACT_NAME
)
from nucypher.blockchain.eth.deployers import StakingEscrowDeployer, StakingInterfaceDeployer
from nucypher.blockchain.eth.registry import InMemoryContractRegistry, LocalContractRegistry
@ -286,7 +286,21 @@ def test_manual_deployment_of_idle_network(click_runner):
deployed_contracts = [NUCYPHER_TOKEN_CONTRACT_NAME]
assert list(new_registry.enrolled_names) == deployed_contracts
# 2. Deploy PolicyManager
# 2. Deploy StakingEscrow in INIT mode
command = ('contracts',
'--contract-name', STAKING_ESCROW_CONTRACT_NAME,
'--mode', 'init',
'--provider', TEST_PROVIDER_URI,
'--network', TEMPORARY_DOMAIN,
'--registry-infile', ALTERNATE_REGISTRY_FILEPATH_2)
result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
deployed_contracts.extend([STAKING_ESCROW_STUB_CONTRACT_NAME, DISPATCHER_CONTRACT_NAME])
assert list(new_registry.enrolled_names) == deployed_contracts
# 3. Deploy PolicyManager
command = ('contracts',
'--contract-name', POLICY_MANAGER_CONTRACT_NAME,
'--provider', TEST_PROVIDER_URI,
@ -299,7 +313,7 @@ def test_manual_deployment_of_idle_network(click_runner):
deployed_contracts.extend([POLICY_MANAGER_CONTRACT_NAME, DISPATCHER_CONTRACT_NAME])
assert list(new_registry.enrolled_names) == deployed_contracts
# 3. Deploy Adjudicator
# 4. Deploy Adjudicator
command = ('contracts',
'--contract-name', ADJUDICATOR_CONTRACT_NAME,
'--provider', TEST_PROVIDER_URI,
@ -312,7 +326,7 @@ def test_manual_deployment_of_idle_network(click_runner):
deployed_contracts.extend([ADJUDICATOR_CONTRACT_NAME, DISPATCHER_CONTRACT_NAME])
assert list(new_registry.enrolled_names) == deployed_contracts
# 4. Deploy StakingEscrow in IDLE mode
# 5. Deploy StakingEscrow in IDLE mode
command = ('contracts',
'--contract-name', STAKING_ESCROW_CONTRACT_NAME,
'--mode', 'idle',
@ -323,10 +337,10 @@ def test_manual_deployment_of_idle_network(click_runner):
result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
deployed_contracts.extend([STAKING_ESCROW_CONTRACT_NAME, DISPATCHER_CONTRACT_NAME])
deployed_contracts.extend([STAKING_ESCROW_CONTRACT_NAME])
assert list(new_registry.enrolled_names) == deployed_contracts
# 5. Activate StakingEscrow
# 6. Activate StakingEscrow
command = ('contracts',
'--contract-name', STAKING_ESCROW_CONTRACT_NAME,
'--activate',

View File

@ -12,7 +12,8 @@ import "contracts/StakingEscrow.sol";
*/
contract PolicyManagerBad is PolicyManager {
constructor(StakingEscrow _escrow, uint32 _secondsPerPeriod) PolicyManager(_escrow, _secondsPerPeriod) {}
constructor(StakingEscrow _escrow) PolicyManager(_escrow) {
}
function getNodeFeeDelta(address, uint16) public view override returns (int256) {}
@ -26,7 +27,8 @@ contract PolicyManagerV2Mock is PolicyManager {
uint256 public valueToCheck;
constructor(StakingEscrow _escrow, uint32 _secondsPerPeriod) PolicyManager(_escrow, _secondsPerPeriod) {}
constructor(StakingEscrow _escrow) PolicyManager(_escrow) {
}
function setValueToCheck(uint256 _valueToCheck) public {
valueToCheck = _valueToCheck;
@ -142,7 +144,8 @@ contract StakingEscrowForPolicyMock {
*/
contract ExtendedPolicyManager is PolicyManager {
constructor(StakingEscrow _escrow, uint32 _secondsPerPeriod) PolicyManager(_escrow, _secondsPerPeriod) {}
constructor(StakingEscrow _escrow) PolicyManager(_escrow) {
}
function setNodeFeeDelta(address _node, uint16 _period, int256 _value) external {
NodeInfo storage node = nodes[_node];

View File

@ -14,6 +14,9 @@ contract EnhancedStakingEscrow is StakingEscrow {
constructor(
NuCypherToken _token,
PolicyManagerInterface _policyManager,
AdjudicatorInterface _adjudicator,
WorkLockInterface _workLock,
uint32 _hoursPerPeriod,
uint256 _issuanceDecayCoefficient,
uint256 _lockDurationCoefficient1,
@ -24,13 +27,13 @@ contract EnhancedStakingEscrow is StakingEscrow {
uint16 _minLockedPeriods,
uint256 _minAllowableLockedTokens,
uint256 _maxAllowableLockedTokens,
uint16 _minWorkerPeriods,
PolicyManagerInterface _policyManager,
AdjudicatorInterface _adjudicator,
WorkLockInterface _workLock
uint16 _minWorkerPeriods
)
StakingEscrow(
_token,
_policyManager,
_adjudicator,
_workLock,
_hoursPerPeriod,
_issuanceDecayCoefficient,
_lockDurationCoefficient1,
@ -41,10 +44,7 @@ contract EnhancedStakingEscrow is StakingEscrow {
_minLockedPeriods,
_minAllowableLockedTokens,
_maxAllowableLockedTokens,
_minWorkerPeriods,
_policyManager,
_adjudicator,
_workLock
_minWorkerPeriods
)
{
}
@ -74,6 +74,9 @@ contract StakingEscrowBad is StakingEscrow {
constructor(
NuCypherToken _token,
PolicyManagerInterface _policyManager,
AdjudicatorInterface _adjudicator,
WorkLockInterface _workLock,
uint32 _hoursPerPeriod,
uint256 _issuanceDecayCoefficient,
uint256 _lockDurationCoefficient1,
@ -84,13 +87,13 @@ contract StakingEscrowBad is StakingEscrow {
uint16 _minLockedPeriods,
uint256 _minAllowableLockedTokens,
uint256 _maxAllowableLockedTokens,
uint16 _minWorkerPeriods,
PolicyManagerInterface _policyManager,
AdjudicatorInterface _adjudicator,
WorkLockInterface _workLock
uint16 _minWorkerPeriods
)
StakingEscrow(
_token,
_policyManager,
_adjudicator,
_workLock,
_hoursPerPeriod,
_issuanceDecayCoefficient,
_lockDurationCoefficient1,
@ -101,10 +104,7 @@ contract StakingEscrowBad is StakingEscrow {
_minLockedPeriods,
_minAllowableLockedTokens,
_maxAllowableLockedTokens,
_minWorkerPeriods,
_policyManager,
_adjudicator,
_workLock
_minWorkerPeriods
)
{
}
@ -123,6 +123,9 @@ contract StakingEscrowV2Mock is StakingEscrow {
constructor(
NuCypherToken _token,
PolicyManagerInterface _policyManager,
AdjudicatorInterface _adjudicator,
WorkLockInterface _workLock,
uint32 _hoursPerPeriod,
uint256 _issuanceDecayCoefficient,
uint256 _lockDurationCoefficient1,
@ -134,13 +137,13 @@ contract StakingEscrowV2Mock is StakingEscrow {
uint256 _minAllowableLockedTokens,
uint256 _maxAllowableLockedTokens,
uint16 _minWorkerPeriods,
PolicyManagerInterface _policyManager,
AdjudicatorInterface _adjudicator,
WorkLockInterface _workLock,
uint256 _valueToCheck
)
StakingEscrow(
_token,
_policyManager,
_adjudicator,
_workLock,
_hoursPerPeriod,
_issuanceDecayCoefficient,
_lockDurationCoefficient1,
@ -151,10 +154,7 @@ contract StakingEscrowV2Mock is StakingEscrow {
_minLockedPeriods,
_minAllowableLockedTokens,
_maxAllowableLockedTokens,
_minWorkerPeriods,
_policyManager,
_adjudicator,
_workLock
_minWorkerPeriods
)
{
valueToCheck = _valueToCheck;

View File

@ -70,8 +70,20 @@ def token(token_economics, deploy_contract):
@pytest.fixture(scope='module')
def policy_manager_bare(testerchain, token_economics, deploy_contract):
contract, _ = deploy_contract('PolicyManager', NULL_ADDRESS, token_economics.seconds_per_period)
def escrow_dispatcher(testerchain, token, token_economics, deploy_contract):
escrow_stub, _ = deploy_contract('StakingEscrowStub',
token.address,
token_economics.hours_per_period,
token_economics.minimum_locked_periods,
token_economics.minimum_allowed_locked,
token_economics.maximum_allowed_locked)
dispatcher, _ = deploy_contract('Dispatcher', escrow_stub.address)
return dispatcher
@pytest.fixture(scope='module')
def policy_manager_bare(testerchain, escrow_dispatcher, deploy_contract):
contract, _ = deploy_contract('PolicyManager', escrow_dispatcher.address)
return contract
@ -89,14 +101,15 @@ def policy_manager(testerchain, policy_manager_bare, policy_manager_dispatcher):
abi=policy_manager_bare.abi,
address=policy_manager_dispatcher.address,
ContractFactoryClass=Contract)
return contract
@pytest.fixture(scope='module')
def adjudicator_bare(testerchain, token_economics, deploy_contract):
def adjudicator_bare(testerchain, token_economics, escrow_dispatcher, deploy_contract):
contract, _ = deploy_contract(
'Adjudicator',
NULL_ADDRESS,
escrow_dispatcher.address,
*token_economics.slashing_deployment_parameters)
return contract
@ -114,11 +127,12 @@ def adjudicator(testerchain, adjudicator_bare, adjudicator_dispatcher):
abi=adjudicator_bare.abi,
address=adjudicator_dispatcher.address,
ContractFactoryClass=Contract)
return contract
@pytest.fixture(scope='module')
def worklock(testerchain, token, token_economics, deploy_contract):
def worklock(testerchain, token, escrow_dispatcher, token_economics, deploy_contract):
# Creator deploys the worklock using test values
now = testerchain.w3.eth.getBlock('latest').timestamp
start_bid_date = ((now + 3600) // 3600 + 1) * 3600 # beginning of the next hour plus 1 hour
@ -130,6 +144,7 @@ def worklock(testerchain, token, token_economics, deploy_contract):
contract, _ = deploy_contract(
contract_name='WorkLock',
_token=token.address,
_escrow=escrow_dispatcher.address,
_startBidDate=start_bid_date,
_endBidDate=end_bid_date,
_endCancellationDate=end_cancellation_date,
@ -137,44 +152,42 @@ def worklock(testerchain, token, token_economics, deploy_contract):
_stakingPeriods=staking_periods,
_minAllowedBid=min_allowed_bid
)
return contract
@pytest.fixture(scope='module')
def escrow_bare(testerchain, token, token_economics, policy_manager, adjudicator, worklock, deploy_contract):
def escrow_bare(testerchain,
token,
policy_manager,
adjudicator,
worklock,
token_economics,
escrow_dispatcher,
deploy_contract):
# Creator deploys the escrow
contract, _ = deploy_contract(
'EnhancedStakingEscrow',
token.address,
*token_economics.staking_deployment_parameters,
policy_manager.address,
adjudicator.address,
worklock.address
worklock.address,
*token_economics.staking_deployment_parameters
)
tx = escrow_dispatcher.functions.upgrade(contract.address).transact()
testerchain.wait_for_receipt(tx)
return contract
@pytest.fixture(scope='module')
def escrow_dispatcher(testerchain, escrow_bare, deploy_contract):
dispatcher, _ = deploy_contract('Dispatcher', escrow_bare.address)
return dispatcher
@pytest.fixture(scope='module')
def escrow(testerchain, escrow_bare, policy_manager, adjudicator, worklock, escrow_dispatcher):
def escrow(testerchain, escrow_bare, escrow_dispatcher):
# Wrap dispatcher contract
contract = testerchain.client.get_contract(
abi=escrow_bare.abi,
address=escrow_dispatcher.address,
ContractFactoryClass=Contract)
tx = policy_manager.functions.setStakingEscrow(contract.address).transact()
testerchain.wait_for_receipt(tx)
tx = adjudicator.functions.setStakingEscrow(contract.address).transact()
testerchain.wait_for_receipt(tx)
tx = worklock.functions.setStakingEscrow(contract.address).transact()
testerchain.wait_for_receipt(tx)
return contract
@ -903,8 +916,8 @@ def test_upgrading_and_rollback(testerchain,
policy_manager_dispatcher,
staking_interface_router,
multisig,
worklock,
adjudicator,
worklock,
deploy_contract):
creator, staker1, staker2, staker3, staker4, alice1, alice2, *contracts_owners =\
testerchain.client.accounts
@ -917,12 +930,12 @@ def test_upgrading_and_rollback(testerchain,
escrow_v2, _ = deploy_contract(
'StakingEscrow',
token.address,
*token_economics.staking_deployment_parameters,
policy_manager.address,
adjudicator.address,
worklock.address
worklock.address,
*token_economics.staking_deployment_parameters
)
policy_manager_v2, _ = deploy_contract('PolicyManager', escrow.address, 0)
policy_manager_v2, _ = deploy_contract('PolicyManager', escrow.address)
# Staker and Alice can't upgrade contracts, only owner can
with pytest.raises((TransactionFailed, ValueError)):
tx = escrow_dispatcher.functions.upgrade(escrow_v2.address).transact({'from': alice1})

View File

@ -32,7 +32,7 @@ def policy_manager(testerchain, escrow, request, deploy_contract):
creator, client, bad_node, node1, node2, node3, *everyone_else = testerchain.client.accounts
# Creator deploys the policy manager
contract, _ = deploy_contract('PolicyManager', escrow.address, 0)
contract, _ = deploy_contract('PolicyManager', escrow.address)
# Give client some ether
tx = testerchain.client.send_transaction(

View File

@ -522,18 +522,18 @@ def test_upgrading(testerchain, deploy_contract):
# Only escrow contract is allowed in PolicyManager constructor
with pytest.raises((TransactionFailed, ValueError)):
deploy_contract('PolicyManager', creator, 0)
deploy_contract('PolicyManager', creator)
# Deploy contracts
escrow1, _ = deploy_contract('StakingEscrowForPolicyMock', 1)
escrow2, _ = deploy_contract('StakingEscrowForPolicyMock', 1)
address1 = escrow1.address
address2 = escrow2.address
contract_library_v1, _ = deploy_contract('PolicyManager', address1, 0)
contract_library_v1, _ = deploy_contract('PolicyManager', address1)
dispatcher, _ = deploy_contract('Dispatcher', contract_library_v1.address)
# Deploy second version of the contract
contract_library_v2, _ = deploy_contract('PolicyManagerV2Mock', address2, 0)
contract_library_v2, _ = deploy_contract('PolicyManagerV2Mock', address2)
contract = testerchain.client.get_contract(
abi=contract_library_v2.abi,
address=dispatcher.address,
@ -562,7 +562,7 @@ def test_upgrading(testerchain, deploy_contract):
assert 3 == contract.functions.valueToCheck().call()
# Can't upgrade to the previous version or to the bad version
contract_library_bad, _ = deploy_contract('PolicyManagerBad', address2, 0)
contract_library_bad, _ = deploy_contract('PolicyManagerBad', address2)
with pytest.raises((TransactionFailed, ValueError)):
tx = dispatcher.functions.upgrade(contract_library_v1.address).transact({'from': creator})
testerchain.wait_for_receipt(tx)
@ -616,7 +616,7 @@ def test_handling_wrong_state(testerchain, deploy_contract):
# Prepare enhanced version of contract
escrow, _ = deploy_contract('StakingEscrowForPolicyMock', 1)
policy_manager, _ = deploy_contract('ExtendedPolicyManager', escrow.address, 0)
policy_manager, _ = deploy_contract('ExtendedPolicyManager', escrow.address)
tx = escrow.functions.setPolicyManager(policy_manager.address).transact({'from': creator})
testerchain.wait_for_receipt(tx)

View File

@ -84,11 +84,12 @@ def escrow_contract(testerchain,
deploy_parameters[5] = 0
deploy_parameters[6] = 0
deploy_parameters.append(policy_manager.address)
deploy_parameters.append(adjudicator.address)
deploy_parameters.append(worklock.address)
contract, _ = deploy_contract('EnhancedStakingEscrow', token.address, *deploy_parameters)
contract, _ = deploy_contract('EnhancedStakingEscrow',
token.address,
policy_manager.address,
adjudicator.address,
worklock.address,
*deploy_parameters)
if request.param:
dispatcher, _ = deploy_contract('Dispatcher', contract.address)

View File

@ -45,10 +45,10 @@ def test_upgrading(testerchain, token, token_economics, deploy_contract):
contract_library_v1, _ = deploy_contract(
'StakingEscrow',
token.address,
*token_economics.staking_deployment_parameters,
policy_manager.address,
adjudicator.address,
worklock.address
worklock.address,
*token_economics.staking_deployment_parameters
)
dispatcher, _ = deploy_contract('Dispatcher', contract_library_v1.address)

View File

@ -59,6 +59,7 @@ def worklock_factory(testerchain, token, escrow, token_economics, deploy_contrac
contract, _ = deploy_contract(
contract_name='WorkLock',
_token=token.address,
_escrow=escrow.address,
_startBidDate=start_bid_date,
_endBidDate=end_bid_date,
_endCancellationDate=end_cancellation_date,
@ -67,9 +68,6 @@ def worklock_factory(testerchain, token, escrow, token_economics, deploy_contrac
_minAllowedBid=MIN_ALLOWED_BID
)
tx = contract.functions.setStakingEscrow(escrow.address).transact()
testerchain.wait_for_receipt(tx)
if supply > 0:
tx = token.functions.approve(contract.address, supply).transact()
testerchain.wait_for_receipt(tx)
@ -863,7 +861,7 @@ def test_verifying_correctness(testerchain, token_economics, escrow, deploy_cont
# Set gas only for one check
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state)\
.transact({'gas': gas_to_save_state + 32000, 'gas_price': 0})
.transact({'gas': gas_to_save_state + 30000, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
assert worklock.functions.nextBidderToCheck().call() == 1
@ -1053,7 +1051,7 @@ def test_force_refund(testerchain, token_economics, deploy_contract, worklock_fa
# But can verify only one of them
assert worklock.functions.nextBidderToCheck().call() == 0
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state)\
.transact({'gas': gas_to_save_state + 32000, 'gas_price': 0})
.transact({'gas': gas_to_save_state + 30000, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
assert worklock.functions.nextBidderToCheck().call() == 1

View File

@ -20,6 +20,7 @@ import os
from pathlib import Path
import requests
from constant_sorrow import constants
from web3.exceptions import ValidationError
from nucypher.blockchain.eth.deployers import AdjudicatorDeployer, BaseContractDeployer, NucypherTokenDeployer, \
@ -83,27 +84,43 @@ CONSTRUCTOR_OVERRIDES = {
"_firstPhaseMaxIssuance": None,
"_miningCoefficient": 2,
"_lockedPeriodsCoefficient": 1,
"_rewardedPeriods": 1},
"v5.6.1": {"_isTestContract": True}
},
PolicyManagerDeployer.contract_name: {"v6.2.1": {"_secondsPerPeriod": None}}
"_rewardedPeriods": 1}
}
}
FORCE_SKIP = {
StakingEscrowDeployer.contract_name: ["v5.6.1"]
}
def deploy_earliest_contract(blockchain_interface: BlockchainDeployerInterface,
deployer: BaseContractDeployer):
def deploy_base_contract(blockchain_interface: BlockchainDeployerInterface,
deployer: BaseContractDeployer,
skipt_test: bool):
contract_name = deployer.contract_name
latest_version, _data = blockchain_interface.find_raw_contract_data(contract_name, "latest")
try:
overrides = CONSTRUCTOR_OVERRIDES[contract_name][latest_version]
except KeyError:
overrides = dict()
version = "latest" if skipt_test else "earliest"
try:
deployer.deploy(contract_version="earliest", **overrides)
deployer.deploy(contract_version=version, deployment_mode=constants.FULL, **overrides)
except ValidationError:
pass # Skip errors related to initialization
def skip_test(blockchain_interface: BlockchainDeployerInterface, contract_name: str):
latest_version, _data = blockchain_interface.find_raw_contract_data(contract_name, "latest")
raw_contracts = blockchain_interface._raw_contract_cache
try:
force_skip = latest_version in FORCE_SKIP[contract_name]
except KeyError:
force_skip = False
return force_skip or len(raw_contracts[contract_name]) == 1
def test_upgradeability(temp_dir_path):
# Prepare remote source for compilation
download_github_dir(GITHUB_SOURCE_LINK, temp_dir_path)
@ -126,15 +143,14 @@ def test_upgradeability(temp_dir_path):
economics = make_token_economics(blockchain_interface)
# 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
skip_adjudicator_test = skip_test(blockchain_interface, contract_name)
contract_name = StakingEscrowDeployer.contract_name
test_staking_escrow = len(raw_contracts[contract_name]) > 1
skip_staking_escrow_test = skip_test(blockchain_interface, contract_name)
contract_name = PolicyManagerDeployer.contract_name
test_policy_manager = len(raw_contracts[contract_name]) > 1
skip_policy_manager_test = skip_test(blockchain_interface, contract_name)
if not test_adjudicator and not test_staking_escrow and not test_policy_manager:
if not skip_adjudicator_test and not skip_staking_escrow_test and not skip_policy_manager_test:
return
# Prepare master version of contracts and upgrade to the latest
@ -145,36 +161,42 @@ def test_upgradeability(temp_dir_path):
economics=economics)
token_deployer.deploy()
staking_escrow_deployer = StakingEscrowDeployer(registry=registry,
deployer_address=origin,
economics=economics)
staking_escrow_deployer.deploy(deployment_mode=constants.INIT)
policy_manager_deployer = PolicyManagerDeployer(registry=registry,
deployer_address=origin,
economics=economics)
deploy_earliest_contract(blockchain_interface, policy_manager_deployer)
deploy_base_contract(blockchain_interface, policy_manager_deployer, skipt_test=skip_policy_manager_test)
adjudicator_deployer = AdjudicatorDeployer(registry=registry,
deployer_address=origin,
economics=economics)
deploy_earliest_contract(blockchain_interface, adjudicator_deployer)
deploy_base_contract(blockchain_interface, adjudicator_deployer, skipt_test=skip_adjudicator_test)
worklock_deployer = WorklockDeployer(registry=registry,
deployer_address=origin,
economics=economics)
worklock_deployer.deploy()
if skip_staking_escrow_test:
worklock_deployer = WorklockDeployer(registry=registry,
deployer_address=origin,
economics=economics)
worklock_deployer.deploy()
staking_escrow_deployer = StakingEscrowDeployer(registry=registry,
deployer_address=origin,
economics=economics)
deploy_earliest_contract(blockchain_interface, staking_escrow_deployer)
deploy_base_contract(blockchain_interface, staking_escrow_deployer, skipt_test=skip_staking_escrow_test)
if test_policy_manager:
policy_manager_deployer.upgrade(contract_version="latest", confirmations=0)
if test_adjudicator:
adjudicator_deployer.upgrade(contract_version="latest", confirmations=0)
if test_staking_escrow:
if not skip_staking_escrow_test:
# TODO prepare at least one staker before calling upgrade
staking_escrow_deployer.upgrade(contract_version="latest", confirmations=0)
if not skip_policy_manager_test:
policy_manager_deployer.upgrade(contract_version="latest", confirmations=0)
if not skip_adjudicator_test:
adjudicator_deployer.upgrade(contract_version="latest", confirmations=0)
finally:
# Unregister interface # TODO: Move to method?
with contextlib.suppress(KeyError):

View File

@ -108,6 +108,7 @@ from tests.utils.ursula import (
MOCK_KNOWN_URSULAS_CACHE,
_mock_ursula_reencrypts
)
from constant_sorrow.constants import (FULL, INIT)
test_logger = Logger("test-logger")
@ -555,6 +556,11 @@ def _make_agency(testerchain,
registry=test_registry)
token_deployer.deploy()
staking_escrow_deployer = StakingEscrowDeployer(deployer_address=origin,
economics=token_economics,
registry=test_registry)
staking_escrow_deployer.deploy(deployment_mode=INIT)
policy_manager_deployer = PolicyManagerDeployer(deployer_address=origin,
economics=token_economics,
registry=test_registry)
@ -565,6 +571,11 @@ def _make_agency(testerchain,
registry=test_registry)
adjudicator_deployer.deploy()
staking_interface_deployer = StakingInterfaceDeployer(deployer_address=origin,
economics=token_economics,
registry=test_registry)
staking_interface_deployer.deploy()
worklock_deployer = WorklockDeployer(deployer_address=origin,
economics=token_economics,
registry=test_registry)
@ -573,12 +584,7 @@ def _make_agency(testerchain,
staking_escrow_deployer = StakingEscrowDeployer(deployer_address=origin,
economics=token_economics,
registry=test_registry)
staking_escrow_deployer.deploy()
staking_interface_deployer = StakingInterfaceDeployer(deployer_address=origin,
economics=token_economics,
registry=test_registry)
staking_interface_deployer.deploy()
staking_escrow_deployer.deploy(deployment_mode=FULL)
token_agent = token_deployer.make_agent() # 1 Token
staking_agent = staking_escrow_deployer.make_agent() # 2 Staking Escrow

View File

@ -26,6 +26,7 @@ from web3 import Web3
from nucypher.blockchain.economics import BaseEconomics, StandardTokenEconomics
from nucypher.blockchain.eth.actors import ContractAdministrator
from nucypher.blockchain.eth.deployers import StakingEscrowDeployer
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry, BaseContractRegistry
from nucypher.blockchain.eth.sol.compile.constants import TEST_SOLIDITY_SOURCE_ROOT, SOLIDITY_SOURCE_ROOT
@ -43,6 +44,7 @@ from tests.constants import (
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS,
PYEVM_DEV_URI
)
from constant_sorrow.constants import (INIT)
def token_airdrop(token_agent, amount: NU, origin: str, addresses: List[str]):
@ -231,10 +233,13 @@ class TesterBlockchain(BlockchainDeployerInterface):
gas_limit = None # TODO: Gas management - #842
for deployer_class in admin.primary_deployer_classes:
if deployer_class in ContractAdministrator.standard_deployer_classes:
admin.deploy_contract(contract_name=deployer_class.contract_name, gas_limit=gas_limit)
if deployer_class is StakingEscrowDeployer:
admin.deploy_contract(contract_name=deployer_class.contract_name,
gas_limit=gas_limit,
deployment_mode=INIT)
else:
admin.deploy_contract(contract_name=deployer_class.contract_name, gas_limit=gas_limit)
admin.deploy_contract(contract_name=StakingEscrowDeployer.contract_name, gas_limit=gas_limit)
return testerchain, registry
@property