Merge pull request #283 from szotov/solidity-dev

Contracts optimization
pull/287/merge
K Prasch 2018-06-04 13:54:39 -07:00 committed by GitHub
commit 0fae344116
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 639 additions and 408 deletions

View File

@ -23,7 +23,7 @@ contract Issuer is Upgradeable {
uint256 public awardedPeriods;
uint256 public lastMintedPeriod;
uint256 public futureSupply;
uint256 public totalSupply;
/**
* Current supply is used in the mining formula and is stored to prevent different calculation
* for miners which get reward in the same period. There are two values -
@ -37,7 +37,7 @@ contract Issuer is Upgradeable {
/**
* @notice Constructor sets address of token contract and coefficients for mining
* @dev Formula for mining in one period
(futureSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / k2
(totalSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / k2
if allLockedPeriods > awardedPeriods then allLockedPeriods = awardedPeriods
* @param _token Token contract
* @param _hoursPerPeriod Size of period in hours
@ -88,9 +88,9 @@ contract Issuer is Upgradeable {
function initialize() public {
require(currentSupply1 == 0);
lastMintedPeriod = getCurrentPeriod();
futureSupply = token.totalSupply();
totalSupply = token.totalSupply();
uint256 reservedReward = token.balanceOf(address(this));
uint256 currentTotalSupply = futureSupply.sub(reservedReward);
uint256 currentTotalSupply = totalSupply.sub(reservedReward);
currentSupply1 = currentTotalSupply;
currentSupply2 = currentTotalSupply;
emit Initialized(reservedReward);
@ -102,32 +102,31 @@ contract Issuer is Upgradeable {
* @param _lockedValue The amount of tokens that were locked by user in specified period.
* @param _totalLockedValue The amount of tokens that were locked by all users in specified period.
* @param _allLockedPeriods The max amount of periods during which tokens will be locked after specified period.
* @param _decimals The amount of locked tokens and blocks in decimals.
* @return Amount of minted tokens.
*/
// TODO decimals
function mint(
uint256 _period,
uint256 _lockedValue,
uint256 _totalLockedValue,
uint256 _allLockedPeriods,
uint256 _decimals
uint256 _allLockedPeriods
)
internal returns (uint256 amount, uint256 decimals)
internal returns (uint256 amount)
{
// TODO finish method before calculation after end of mining
uint256 currentSupply = _period <= lastMintedPeriod ?
Math.min256(currentSupply1, currentSupply2) :
Math.max256(currentSupply1, currentSupply2);
if (currentSupply == totalSupply) {
return;
}
//futureSupply * lockedValue * (k1 + allLockedPeriods) / (totalLockedValue * k2) -
//totalSupply * lockedValue * (k1 + allLockedPeriods) / (totalLockedValue * k2) -
//currentSupply * lockedValue * (k1 + allLockedPeriods) / (totalLockedValue * k2)
uint256 allLockedPeriods = (_allLockedPeriods <= awardedPeriods ?
_allLockedPeriods : awardedPeriods)
.add(lockedPeriodsCoefficient);
uint256 denominator = _totalLockedValue.mul(miningCoefficient);
amount =
futureSupply
totalSupply
.mul(_lockedValue)
.mul(allLockedPeriods)
.div(denominator).sub(
@ -135,7 +134,10 @@ contract Issuer is Upgradeable {
.mul(_lockedValue)
.mul(allLockedPeriods)
.div(denominator));
decimals = _decimals;
// rounding the last reward
if (amount == 0) {
amount = 1;
}
if (_period <= lastMintedPeriod) {
if (currentSupply1 > currentSupply2) {
@ -162,7 +164,7 @@ contract Issuer is Upgradeable {
require(uint256(delegateGet(_testTarget, "lastMintedPeriod()")) == lastMintedPeriod);
require(uint256(delegateGet(_testTarget, "currentSupply1()")) == currentSupply1);
require(uint256(delegateGet(_testTarget, "currentSupply2()")) == currentSupply2);
require(uint256(delegateGet(_testTarget, "futureSupply()")) == futureSupply);
require(uint256(delegateGet(_testTarget, "totalSupply()")) == totalSupply);
}
function finishUpgrade(address _target) public onlyOwner {

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.23;
pragma solidity ^0.4.24;
import "zeppelin/token/ERC20/SafeERC20.sol";
@ -10,6 +10,7 @@ import "contracts/Issuer.sol";
* @notice PolicyManager interface
**/
contract PolicyManagerInterface {
function register(address _node, uint256 _period) external;
function updateReward(address _node, uint256 _period) external;
function escrow() public view returns (address);
}
@ -50,7 +51,6 @@ contract MinersEscrow is Issuer {
struct MinerInfo {
uint256 value;
uint256 decimals;
/*
* Periods that confirmed but not yet mined, two values instead of array for optimisation.
* lock() and confirmActivity() methods include mint() method so may be only two confirmed
@ -64,7 +64,7 @@ contract MinersEscrow is Issuer {
uint256 lastActivePeriod;
Downtime[] downtime;
StakeInfo[] stakes;
bytes32[] minerIds;
bytes[] minerIds;
}
/*
@ -116,7 +116,8 @@ contract MinersEscrow is Issuer {
_awardedPeriods
)
{
require(_minLockedPeriods != 0 && _maxAllowableLockedTokens != 0);
// constant `1` in the expression `_minLockedPeriods > 1` uses to simplify the `lock` method
require(_minLockedPeriods > 1 && _maxAllowableLockedTokens != 0);
minLockedPeriods = _minLockedPeriods;
minAllowableLockedTokens = _minAllowableLockedTokens;
maxAllowableLockedTokens = _maxAllowableLockedTokens;
@ -137,7 +138,6 @@ contract MinersEscrow is Issuer {
function getStartPeriod(MinerInfo storage _info, uint256 _currentPeriod)
internal view returns (uint256)
{
// TODO try to optimize working with confirmed next period (getLockedTokens, lock, divideStake, mint)
// if next period (after current) is confirmed
if (_info.confirmedPeriod1 > _currentPeriod || _info.confirmedPeriod2 > _currentPeriod) {
return _currentPeriod.add(uint256(1));
@ -157,6 +157,7 @@ contract MinersEscrow is Issuer {
/**
* @notice Get locked tokens value for owner in current period
* @param _owner Tokens owner
* @param _periods Amount of periods to get locked tokens
**/
function getLockedTokens(address _owner, uint256 _periods)
public view returns (uint256 lockedValue)
@ -212,12 +213,12 @@ contract MinersEscrow is Issuer {
uint256 value = _values[i];
uint256 periods = _periods[i];
MinerInfo storage info = minerInfo[miner];
require(info.value == 0 &&
require(info.stakes.length == 0 &&
value >= minAllowableLockedTokens &&
value <= maxAllowableLockedTokens &&
periods >= minLockedPeriods);
miners.push(miner);
info.lastActivePeriod = currentPeriod;
policyManager.register(miner, currentPeriod);
info.value = value;
info.stakes.push(StakeInfo(currentPeriod.add(uint256(1)), 0, periods, value));
allValue = allValue.add(value);
@ -227,6 +228,19 @@ contract MinersEscrow is Issuer {
token.safeTransferFrom(msg.sender, address(this), allValue);
}
/**
* @notice Get last active miner's period
* @param _owner Tokens owner
**/
function getLastActivePeriod(address _owner) public view returns (uint256) {
MinerInfo storage info = minerInfo[_owner];
if (info.confirmedPeriod1 != EMPTY_CONFIRMED_PERIOD ||
info.confirmedPeriod2 != EMPTY_CONFIRMED_PERIOD) {
return Math.max256(info.confirmedPeriod1, info.confirmedPeriod2);
}
return info.lastActivePeriod;
}
/**
* @notice Implementation of the receiveApproval(address,uint256,address,bytes) method
* (see NuCypherToken contract). Deposit all tokens that were approved to transfer
@ -275,9 +289,10 @@ contract MinersEscrow is Issuer {
function deposit(address _owner, uint256 _value, uint256 _periods) internal isInitialized {
require(_value != 0);
MinerInfo storage info = minerInfo[_owner];
if (info.lastActivePeriod == 0) {
// initial stake of the miner
if (info.stakes.length == 0) {
miners.push(_owner);
info.lastActivePeriod = getCurrentPeriod();
policyManager.register(_owner, getCurrentPeriod());
}
info.value = info.value.add(_value);
token.safeTransferFrom(_owner, address(this), _value);
@ -301,29 +316,25 @@ contract MinersEscrow is Issuer {
* @param _periods Amount of periods during which tokens will be locked
**/
function lock(address _owner, uint256 _value, uint256 _periods) internal {
require(_value != 0 || _periods != 0);
require(_value <= token.balanceOf(address(this)) &&
_value >= minAllowableLockedTokens &&
_periods >= minLockedPeriods);
uint256 lastActivePeriod = getLastActivePeriod(_owner);
mint(_owner);
uint256 lockedTokens = getLockedTokens(_owner, 1);
MinerInfo storage info = minerInfo[_owner];
require(_value <= token.balanceOf(address(this)) &&
_value <= info.value.sub(lockedTokens) &&
_value >= minAllowableLockedTokens &&
_value.add(lockedTokens) <= maxAllowableLockedTokens &&
_periods >= minLockedPeriods);
require(_value.add(lockedTokens) <= info.value &&
_value.add(lockedTokens) <= maxAllowableLockedTokens);
uint256 nextPeriod = getCurrentPeriod().add(uint256(1));
if (info.confirmedPeriod1 != nextPeriod && info.confirmedPeriod2 != nextPeriod) {
info.stakes.push(StakeInfo(nextPeriod, 0, _periods, _value));
} else {
if (_periods == 1) {
info.stakes.push(StakeInfo(nextPeriod, nextPeriod, 0, _value));
} else {
info.stakes.push(StakeInfo(nextPeriod, 0, _periods - 1, _value));
}
info.stakes.push(StakeInfo(nextPeriod, 0, _periods - 1, _value));
}
confirmActivity(_owner, _value + lockedTokens, _value);
confirmActivity(_owner, _value + lockedTokens, _value, lastActivePeriod);
emit Locked(_owner, _value, nextPeriod, _periods);
}
@ -342,22 +353,28 @@ contract MinersEscrow is Issuer {
)
public onlyTokenOwner
{
require(_newValue >= minAllowableLockedTokens && _periods > 0);
uint256 currentPeriod = getCurrentPeriod();
require(_newValue >= minAllowableLockedTokens &&
_periods > 0 &&
_lastPeriod >= currentPeriod);
MinerInfo storage info = minerInfo[msg.sender];
uint256 startPeriod = getStartPeriod(info, getCurrentPeriod());
uint256 startPeriod = getStartPeriod(info, currentPeriod);
for (uint256 index = 0; index < info.stakes.length; index++) {
StakeInfo storage stake = info.stakes[index];
uint256 lastPeriod = getLastPeriod(stake, startPeriod);
if (stake.lockedValue == _oldValue &&
lastPeriod == _lastPeriod) {
getLastPeriod(stake, startPeriod) == _lastPeriod) {
break;
}
}
// TODO lastPeriod can be equal current period (if next is confirmed) but need to recalculate in confirmActivity
require(index < info.stakes.length && lastPeriod >= startPeriod);
require(index < info.stakes.length);
stake.lockedValue = stake.lockedValue.sub(_newValue);
require(stake.lockedValue >= minAllowableLockedTokens);
info.stakes.push(StakeInfo(stake.firstPeriod, 0, stake.periods.add(_periods), _newValue));
// if the next period is confirmed and old stake is finishing in the current period then rerun confirmActivity
if (_lastPeriod == currentPeriod && startPeriod > currentPeriod) {
confirmActivity(msg.sender, _newValue, _newValue, 0);
}
emit Divided(msg.sender, _oldValue, _lastPeriod, _newValue, _periods);
emit Locked(msg.sender, _newValue, stake.firstPeriod, stake.periods + _periods);
}
@ -368,7 +385,8 @@ contract MinersEscrow is Issuer {
**/
function withdraw(uint256 _value) public onlyTokenOwner {
MinerInfo storage info = minerInfo[msg.sender];
// TODO optimize
// the max locked tokens in most cases will be in the current period
// but when the miner stakes more then we should use the next period
uint256 lockedTokens = Math.max256(getLockedTokens(msg.sender, 1),
getLockedTokens(msg.sender, 0));
require(_value <= token.balanceOf(address(this)) &&
@ -384,11 +402,18 @@ contract MinersEscrow is Issuer {
* @param _lockedValue Locked tokens in future period
* @param _additional Additional locked tokens in future period.
* Used only if the period has already been confirmed
* @param _lastActivePeriod Last active period
**/
function confirmActivity(address _owner, uint256 _lockedValue, uint256 _additional) internal {
function confirmActivity(
address _owner,
uint256 _lockedValue,
uint256 _additional,
uint256 _lastActivePeriod
) internal {
require(_lockedValue > 0);
MinerInfo storage info = minerInfo[_owner];
uint256 nextPeriod = getCurrentPeriod() + 1;
uint256 currentPeriod = getCurrentPeriod();
uint256 nextPeriod = currentPeriod.add(uint256(1));
// update lockedValue if the period has already been confirmed
if (info.confirmedPeriod1 == nextPeriod) {
@ -418,11 +443,10 @@ contract MinersEscrow is Issuer {
}
}
uint256 currentPeriod = nextPeriod - 1;
if (info.lastActivePeriod < currentPeriod) {
info.downtime.push(Downtime(info.lastActivePeriod + 1, currentPeriod));
// miner is inactive for several periods
if (_lastActivePeriod < currentPeriod) {
info.downtime.push(Downtime(_lastActivePeriod + 1, currentPeriod));
}
info.lastActivePeriod = nextPeriod;
emit ActivityConfirmed(_owner, nextPeriod, _lockedValue);
}
@ -430,6 +454,7 @@ contract MinersEscrow is Issuer {
* @notice Confirm activity for future period and mine for previous period
**/
function confirmActivity() external onlyTokenOwner {
uint256 lastActivePeriod = getLastActivePeriod(msg.sender);
mint(msg.sender);
MinerInfo storage info = minerInfo[msg.sender];
uint256 currentPeriod = getCurrentPeriod();
@ -442,13 +467,21 @@ contract MinersEscrow is Issuer {
}
uint256 lockedTokens = getLockedTokens(msg.sender, 1);
confirmActivity(msg.sender, lockedTokens, 0);
confirmActivity(msg.sender, lockedTokens, 0, lastActivePeriod);
}
/**
* @notice Mint tokens for sender for previous periods if he locked his tokens and confirmed activity
**/
function mint() public onlyTokenOwner {
function mint() external onlyTokenOwner {
// save last active period to the storage if only one period is confirmed
// because after this minting confirmed periods can be empty and can't calculate period from them
// see getLastActivePeriod(address)
MinerInfo storage info = minerInfo[msg.sender];
if (info.confirmedPeriod1 != EMPTY_CONFIRMED_PERIOD ||
info.confirmedPeriod2 != EMPTY_CONFIRMED_PERIOD) {
info.lastActivePeriod = Math.max256(info.confirmedPeriod1, info.confirmedPeriod2);
}
mint(msg.sender);
}
@ -458,7 +491,7 @@ contract MinersEscrow is Issuer {
**/
function mint(address _owner) internal {
uint256 startPeriod = getCurrentPeriod();
uint256 previousPeriod = startPeriod.sub(uint(1));
uint256 previousPeriod = startPeriod.sub(uint256(1));
MinerInfo storage info = minerInfo[_owner];
if (info.confirmedPeriod1 > previousPeriod &&
@ -519,18 +552,15 @@ contract MinersEscrow is Issuer {
)
internal returns (uint256 reward)
{
uint256 amount;
for (uint256 i = 0; i < _info.stakes.length; i++) {
StakeInfo storage stake = _info.stakes[i];
uint256 lastPeriod = getLastPeriod(stake, _startPeriod);
if (stake.firstPeriod <= _period && lastPeriod >= _period) {
(amount, _info.decimals) = mint(
reward = reward.add(mint(
_previousPeriod,
stake.lockedValue,
lockedPerPeriod[_period],
lastPeriod.sub(_period),
_info.decimals);
reward = reward.add(amount);
lastPeriod.sub(_period)));
}
}
policyManager.updateReward(_owner, _period);
@ -599,14 +629,14 @@ contract MinersEscrow is Issuer {
/**
* @notice Return the miner id
**/
function getMinerId(address _miner, uint256 _index) public view returns (bytes32) {
function getMinerId(address _miner, uint256 _index) public view returns (bytes) {
return minerInfo[_miner].minerIds[_index];
}
/**
* @notice Set the miner id
**/
function setMinerId(bytes32 _minerId) public {
function setMinerId(bytes _minerId) public {
MinerInfo storage info = minerInfo[msg.sender];
info.minerIds.push(_minerId);
}
@ -698,6 +728,18 @@ contract MinersEscrow is Issuer {
}
}
/**
* @dev Get miner id bytes by delegatecall
**/
function delegateGetMinerId(address _target, string _signature, address _miner, uint256 _index)
internal returns (bytes memory result)
{
bytes32 memoryAddress = delegateGetData(_target, _signature, 2, bytes32(_miner), bytes32(_index));
assembly {
result := add(memoryAddress, mload(memoryAddress))
}
}
function verifyState(address _testTarget) public onlyOwner {
super.verifyState(_testTarget);
require(uint256(delegateGet(_testTarget, "minLockedPeriods()")) ==
@ -720,7 +762,6 @@ contract MinersEscrow is Issuer {
MinerInfo storage info = minerInfo[minerAddress];
MinerInfo memory infoToCheck = delegateGetMinerInfo(_testTarget, minerAddress);
require(infoToCheck.value == info.value &&
infoToCheck.decimals == info.decimals &&
infoToCheck.confirmedPeriod1 == info.confirmedPeriod1 &&
infoToCheck.confirmedPeriod2 == info.confirmedPeriod2 &&
infoToCheck.lastActivePeriod == info.lastActivePeriod);
@ -745,7 +786,11 @@ contract MinersEscrow is Issuer {
require(uint256(delegateGet(_testTarget, "getMinerIdsLength(address)", miner)) == info.minerIds.length);
for (i = 0; i < info.minerIds.length && i < MAX_CHECKED_VALUES; i++) {
require(delegateGet(_testTarget, "getMinerId(address,uint256)", miner, bytes32(i)) == info.minerIds[i]);
bytes memory minerIdToCheck =
delegateGetMinerId(_testTarget, "getMinerId(address,uint256)", minerAddress, i);
bytes storage minerId = info.minerIds[i];
require(minerIdToCheck.length == minerId.length &&
keccak256(minerIdToCheck) == keccak256(minerId));
}
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.23;
pragma solidity ^0.4.24;
import "zeppelin/token/ERC20/SafeERC20.sol";
@ -49,9 +49,9 @@ contract PolicyManager is Upgradeable {
);
struct ArrangementInfo {
address node;
uint256 indexOfDowntimePeriods;
uint256 lastRefundedPeriod;
bool active;
}
struct Policy {
@ -64,8 +64,7 @@ contract PolicyManager is Upgradeable {
uint256 lastPeriod;
bool disabled;
mapping(address => ArrangementInfo) arrangements;
address[] nodes;
ArrangementInfo[] arrangements;
}
struct NodeInfo {
@ -80,6 +79,7 @@ contract PolicyManager is Upgradeable {
address constant RESERVED_NODE = 0x0;
MinersEscrow public escrow;
uint256 public secondsPerPeriod;
mapping (bytes20 => Policy) public policies;
mapping (address => NodeInfo) public nodes;
@ -90,6 +90,42 @@ contract PolicyManager is Upgradeable {
constructor(MinersEscrow _escrow) public {
require(address(_escrow) != 0x0);
escrow = _escrow;
secondsPerPeriod = escrow.secondsPerPeriod();
}
/**
* @dev Checks that sender is the MinersEscrow contract
**/
modifier onlyEscrowContract()
{
require(msg.sender == address(escrow));
_;
}
/**
* @return Number of current period
**/
function getCurrentPeriod() public view returns (uint256) {
return block.timestamp / secondsPerPeriod;
}
/**
* @notice Register a node
* @param _node Node address
* @param _period Initial period
**/
function register(address _node, uint256 _period) external onlyEscrowContract {
NodeInfo storage nodeInfo = nodes[_node];
require(nodeInfo.lastMinedPeriod == 0);
nodeInfo.lastMinedPeriod = _period;
}
/**
* @notice Set the minimum reward that the node will take
**/
function setMinRewardRate(uint256 _minRewardRate) public {
NodeInfo storage node = nodes[msg.sender];
node.minRewardRate = _minRewardRate;
}
/**
@ -116,34 +152,26 @@ contract PolicyManager is Upgradeable {
);
Policy storage policy = policies[_policyId];
policy.client = msg.sender;
policy.nodes = _nodes;
uint256 currentPeriod = escrow.getCurrentPeriod();
policy.startPeriod = currentPeriod.add(uint(1));
uint256 currentPeriod = getCurrentPeriod();
policy.startPeriod = currentPeriod.add(uint256(1));
policy.lastPeriod = currentPeriod.add(_numberOfPeriods);
policy.rewardRate = msg.value.div(_nodes.length).sub(_firstReward).div(_numberOfPeriods);
policy.firstReward = _firstReward;
require(policy.rewardRate > _firstReward &&
(_firstReward + policy.rewardRate * _numberOfPeriods) * _nodes.length == msg.value);
uint256 endPeriod = policy.lastPeriod.add(uint(1));
uint256 endPeriod = policy.lastPeriod.add(uint256(1));
uint256 startReward = policy.rewardRate - _firstReward;
policy.nodes = _nodes;
for (uint256 i = 0; i < _nodes.length; i++) {
address node = _nodes[i];
require(escrow.getLockedTokens(node) != 0 && node != RESERVED_NODE);
require(node != RESERVED_NODE);
NodeInfo storage nodeInfo = nodes[node];
require(policy.rewardRate >= nodeInfo.minRewardRate);
require(nodeInfo.lastMinedPeriod != 0 && policy.rewardRate >= nodeInfo.minRewardRate);
nodeInfo.rewardDelta[currentPeriod] = nodeInfo.rewardDelta[currentPeriod].add(_firstReward);
nodeInfo.rewardDelta[policy.startPeriod] = nodeInfo.rewardDelta[policy.startPeriod]
.add(startReward);
nodeInfo.rewardDelta[endPeriod] = nodeInfo.rewardDelta[endPeriod].sub(policy.rewardRate);
// TODO node should pay for this
if (nodeInfo.lastMinedPeriod == 0) {
nodeInfo.lastMinedPeriod = currentPeriod.sub(uint256(1));
}
ArrangementInfo storage arrangement = policy.arrangements[node];
arrangement.indexOfDowntimePeriods = escrow.getDowntimeLength(node);
arrangement.active = true;
policy.arrangements.push(ArrangementInfo(node, escrow.getDowntimeLength(node), 0));
}
emit PolicyCreated(_policyId, msg.sender, _nodes);
@ -154,8 +182,7 @@ contract PolicyManager is Upgradeable {
* @param _node Node address
* @param _period Processed period
**/
function updateReward(address _node, uint256 _period) external {
require(msg.sender == address(escrow));
function updateReward(address _node, uint256 _period) external onlyEscrowContract {
NodeInfo storage node = nodes[_node];
if (node.lastMinedPeriod == 0 || _period <= node.lastMinedPeriod) {
return;
@ -180,29 +207,153 @@ contract PolicyManager is Upgradeable {
emit Withdrawn(msg.sender, reward);
}
/**
* @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, uint256 lastRefundedPeriod)
{
uint256 maxPeriod = Math.min256(getCurrentPeriod(), _policy.lastPeriod);
uint256 minPeriod = Math.max256(_policy.startPeriod, _arrangement.lastRefundedPeriod);
uint256 downtimePeriods = 0;
uint256 length = escrow.getDowntimeLength(_arrangement.node);
for (indexOfDowntimePeriods = _arrangement.indexOfDowntimePeriods;
indexOfDowntimePeriods < length;
indexOfDowntimePeriods++)
{
(uint256 startPeriod, uint256 endPeriod) =
escrow.getDowntime(_arrangement.node, indexOfDowntimePeriods);
if (startPeriod > maxPeriod) {
break;
} else if (endPeriod < minPeriod) {
continue;
}
downtimePeriods = downtimePeriods.add(
Math.min256(maxPeriod, endPeriod)
.sub(Math.max256(minPeriod, startPeriod))
.add(uint256(1)));
if (maxPeriod <= endPeriod) {
break;
}
}
uint256 lastActivePeriod = escrow.getLastActivePeriod(_arrangement.node);
if (indexOfDowntimePeriods == length && lastActivePeriod < maxPeriod) {
downtimePeriods = downtimePeriods.add(
maxPeriod.sub(Math.max256(
minPeriod.sub(uint256(1)), lastActivePeriod)));
}
// check activity for the first period
if (_arrangement.lastRefundedPeriod == 0) {
if (lastActivePeriod < _policy.startPeriod - 1) {
refundValue = _policy.firstReward;
} else if (_arrangement.indexOfDowntimePeriods < length) {
(startPeriod, endPeriod) = escrow.getDowntime(_arrangement.node, _arrangement.indexOfDowntimePeriods);
if (_policy.startPeriod > startPeriod && _policy.startPeriod - 1 <= endPeriod) {
refundValue = _policy.firstReward;
}
}
}
refundValue = refundValue.add(_policy.rewardRate.mul(downtimePeriods));
lastRefundedPeriod = maxPeriod.add(uint256(1));
}
/**
* @notice Revoke/refund arrangement/policy by the client
* @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(bytes20 _policyId, address _node, bool _forceRevoke)
internal returns (uint256 refundValue)
{
Policy storage policy = policies[_policyId];
require(policy.client == msg.sender && !policy.disabled);
uint256 endPeriod = policy.lastPeriod.add(uint256(1));
uint256 numberOfActive = policy.arrangements.length;
for (uint256 i = 0; i < policy.arrangements.length; i++) {
ArrangementInfo storage arrangement = policy.arrangements[i];
address node = arrangement.node;
if (node == RESERVED_NODE || _node != RESERVED_NODE && _node != node) {
numberOfActive--;
continue;
}
uint256 nodeRefundValue;
(nodeRefundValue, arrangement.indexOfDowntimePeriods, arrangement.lastRefundedPeriod) =
calculateRefundValue(policy, arrangement);
if (_forceRevoke) {
NodeInfo storage nodeInfo = nodes[node];
nodeInfo.rewardDelta[arrangement.lastRefundedPeriod] =
nodeInfo.rewardDelta[arrangement.lastRefundedPeriod].sub(policy.rewardRate);
nodeInfo.rewardDelta[endPeriod] = nodeInfo.rewardDelta[endPeriod].add(policy.rewardRate);
nodeRefundValue = nodeRefundValue.add(
endPeriod.sub(arrangement.lastRefundedPeriod).mul(policy.rewardRate));
}
if (_forceRevoke || arrangement.lastRefundedPeriod > policy.lastPeriod) {
arrangement.node = RESERVED_NODE;
numberOfActive--;
emit ArrangementRevoked(_policyId, msg.sender, node, nodeRefundValue);
} else {
emit RefundForArrangement(_policyId, msg.sender, node, nodeRefundValue);
}
refundValue = refundValue.add(nodeRefundValue);
if (_node != RESERVED_NODE) {
break;
}
}
if (refundValue > 0) {
msg.sender.transfer(refundValue);
}
if (_node == RESERVED_NODE) {
if (numberOfActive == 0) {
policy.disabled = true;
emit PolicyRevoked(_policyId, msg.sender, refundValue);
} else {
emit RefundForPolicy(_policyId, msg.sender, refundValue);
}
} else {
// arrangement not found
require(i < policy.arrangements.length);
}
}
/**
* @notice Calculate amount of refund
* @param _policyId Policy id
* @param _node Node or RESERVED_NODE if all nodes should be used
**/
function calculateRefundValueInternal(bytes20 _policyId, address _node)
internal view returns (uint256 refundValue)
{
Policy storage policy = policies[_policyId];
require(msg.sender == policy.client && !policy.disabled);
for (uint256 i = 0; i < policy.arrangements.length; i++) {
ArrangementInfo storage arrangement = policy.arrangements[i];
if (arrangement.node == RESERVED_NODE || _node != RESERVED_NODE && _node != arrangement.node) {
continue;
}
(uint256 nodeRefundValue,,) = calculateRefundValue(policy, arrangement);
refundValue = refundValue.add(nodeRefundValue);
if (_node != RESERVED_NODE) {
break;
}
}
if (_node != RESERVED_NODE) {
// arrangement not found
require(i < policy.arrangements.length);
}
}
/**
* @notice Revoke policy by client
* @param _policyId Policy id
**/
function revokePolicy(bytes20 _policyId) public {
Policy storage policy = policies[_policyId];
require(policy.client == msg.sender && !policy.disabled);
uint256 refundValue = 0;
uint256 endPeriod = policy.lastPeriod.add(uint(1));
for (uint256 i = 0; i < policy.nodes.length; i++) {
address node = policy.nodes[i];
if (!policy.arrangements[node].active) {
continue;
}
uint256 nodeRefundValue = revokeArrangement(policy, node, endPeriod);
refundValue = refundValue.add(nodeRefundValue);
emit ArrangementRevoked(_policyId, msg.sender, node, nodeRefundValue);
}
policy.disabled = true;
if (refundValue > 0) {
msg.sender.transfer(refundValue);
}
emit PolicyRevoked(_policyId, msg.sender, refundValue);
refundInternal(_policyId, RESERVED_NODE, true);
}
/**
@ -213,36 +364,8 @@ contract PolicyManager is Upgradeable {
function revokeArrangement(bytes20 _policyId, address _node)
public returns (uint256 refundValue)
{
Policy storage policy = policies[_policyId];
require(policy.client == msg.sender &&
!policy.disabled &&
policy.arrangements[_node].active);
uint256 endPeriod = policy.lastPeriod.add(uint(1));
refundValue = revokeArrangement(policy, _node, endPeriod);
if (refundValue > 0) {
msg.sender.transfer(refundValue);
}
emit ArrangementRevoked(_policyId, msg.sender, _node, refundValue);
}
/**
* @notice Revoke arrangement by client
* @param _policy Policy
* @param _node Node that will be excluded
* @param _endPeriod Pre-calculated end of period value
**/
function revokeArrangement(Policy storage _policy, address _node, uint256 _endPeriod)
internal returns (uint256 refundValue)
{
refundValue = calculateRefund(_policy, _node);
NodeInfo storage node = nodes[_node];
ArrangementInfo storage arrangement = _policy.arrangements[_node];
node.rewardDelta[arrangement.lastRefundedPeriod] =
node.rewardDelta[arrangement.lastRefundedPeriod].sub(_policy.rewardRate);
node.rewardDelta[_endPeriod] = node.rewardDelta[_endPeriod].add(_policy.rewardRate);
refundValue = refundValue.add(
_endPeriod.sub(arrangement.lastRefundedPeriod).mul(_policy.rewardRate));
_policy.arrangements[_node].active = false;
require(_node != RESERVED_NODE);
return refundInternal(_policyId, _node, true);
}
/**
@ -250,31 +373,7 @@ contract PolicyManager is Upgradeable {
* @param _policyId Policy id
**/
function refund(bytes20 _policyId) public {
Policy storage policy = policies[_policyId];
require(msg.sender == policy.client && !policy.disabled);
uint256 refundValue = 0;
uint256 numberOfActive = policy.nodes.length;
for (uint256 i = 0; i < policy.nodes.length; i++) {
address node = policy.nodes[i];
if (!policy.arrangements[node].active) {
numberOfActive--;
continue;
}
uint256 nodeRefundValue = calculateRefund(policy, node);
if (policy.arrangements[node].lastRefundedPeriod > policy.lastPeriod) {
policy.arrangements[node].active = false;
numberOfActive--;
}
refundValue = refundValue.add(nodeRefundValue);
emit RefundForArrangement(_policyId, msg.sender, node, nodeRefundValue);
}
if (refundValue > 0) {
msg.sender.transfer(refundValue);
}
if (numberOfActive == 0) {
policy.disabled = true;
}
emit RefundForPolicy(_policyId, msg.sender, refundValue);
refundInternal(_policyId, RESERVED_NODE, false);
}
/**
@ -285,102 +384,40 @@ contract PolicyManager is Upgradeable {
function refund(bytes20 _policyId, address _node)
public returns (uint256 refundValue)
{
Policy storage policy = policies[_policyId];
require(msg.sender == policy.client &&
!policy.disabled &&
policy.arrangements[_node].active);
refundValue = calculateRefund(policy, _node);
if (policy.arrangements[_node].lastRefundedPeriod > policy.lastPeriod) {
policy.arrangements[_node].active = false;
}
if (refundValue > 0) {
msg.sender.transfer(refundValue);
}
emit RefundForArrangement(_policyId, msg.sender, _node, refundValue);
require(_node != RESERVED_NODE);
return refundInternal(_policyId, _node, false);
}
/**
* @notice Calculate amount of refund
* @param _policy Policy
* @param _node Node for calculation
**/
//TODO extract checkRefund method
function calculateRefund(Policy storage _policy, address _node) internal returns (uint256 refundValue) {
ArrangementInfo storage arrangement = _policy.arrangements[_node];
uint256 maxPeriod = Math.min256(escrow.getCurrentPeriod(), _policy.lastPeriod);
uint256 minPeriod = Math.max256(_policy.startPeriod, arrangement.lastRefundedPeriod);
uint256 downtimePeriods = 0;
uint256 length = escrow.getDowntimeLength(_node);
for (uint256 i = arrangement.indexOfDowntimePeriods; i < length; i++) {
uint256 startPeriod;
uint256 endPeriod;
(startPeriod, endPeriod) = escrow.getDowntime(_node, i);
if (startPeriod > maxPeriod) {
break;
} else if (endPeriod < minPeriod) {
continue;
}
downtimePeriods = downtimePeriods.add(
Math.min256(maxPeriod, endPeriod)
.sub(Math.max256(minPeriod, startPeriod))
.add(uint(1)));
if (maxPeriod <= endPeriod) {
break;
}
}
uint256 lastActivePeriod;
(,,,,lastActivePeriod) = escrow.minerInfo(_node);
if (i == length && lastActivePeriod < maxPeriod) {
downtimePeriods = downtimePeriods.add(
maxPeriod.sub(Math.max256(
minPeriod.sub(uint(1)), lastActivePeriod)));
}
// TODO refactor
if (arrangement.lastRefundedPeriod == 0) {
if (lastActivePeriod < _policy.startPeriod - 1) {
refundValue = _policy.firstReward;
} else if (arrangement.indexOfDowntimePeriods < length) {
(startPeriod, endPeriod) = escrow.getDowntime(_node, arrangement.indexOfDowntimePeriods);
if (_policy.startPeriod > startPeriod && _policy.startPeriod - 1 <= endPeriod) {
refundValue = _policy.firstReward;
}
}
}
refundValue = refundValue.add(_policy.rewardRate.mul(downtimePeriods));
arrangement.indexOfDowntimePeriods = i;
arrangement.lastRefundedPeriod = maxPeriod.add(uint(1));
}
/**
* @notice Set the minimum reward that the node will take
**/
function setMinRewardRate(uint256 _minRewardRate) public {
NodeInfo storage node = nodes[msg.sender];
node.minRewardRate = _minRewardRate;
}
/**
* @notice Get number of nodes in policy
* @param _policyId Policy id
**/
function getPolicyNodesLength(bytes20 _policyId)
function calculateRefundValue(bytes20 _policyId)
external view returns (uint256 refundValue)
{
return calculateRefundValueInternal(_policyId, RESERVED_NODE);
}
/**
* @notice Calculate amount of refund
* @param _policyId Policy id
* @param _node Node
**/
function calculateRefundValue(bytes20 _policyId, address _node)
external view returns (uint256 refundValue)
{
require(_node != RESERVED_NODE);
return calculateRefundValueInternal(_policyId, _node);
}
/**
* @notice Get number of arrangements in the policy
* @param _policyId Policy id
**/
function getArrangementsLength(bytes20 _policyId)
public view returns (uint256)
{
return policies[_policyId].nodes.length;
}
/**
* @notice Get node from policy
* @param _policyId Policy id
* @param _index Index of node
**/
function getPolicyNode(bytes20 _policyId, uint256 _index)
public view returns (address)
{
return policies[_policyId].nodes[_index];
return policies[_policyId].arrangements.length;
}
/**
@ -397,15 +434,15 @@ contract PolicyManager is Upgradeable {
/**
* @notice Return the information about arrangement
**/
function getArrangementInfo(bytes20 _policyId, address _node)
function getArrangementInfo(bytes20 _policyId, uint256 _index)
// TODO change to structure when ABIEncoderV2 is released
// public view returns (ArrangementInfo)
public view returns (uint256 indexOfDowntimePeriods, uint256 lastRefundedPeriod, bool active)
public view returns (address node, uint256 indexOfDowntimePeriods, uint256 lastRefundedPeriod)
{
ArrangementInfo storage info = policies[_policyId].arrangements[_node];
ArrangementInfo storage info = policies[_policyId].arrangements[_index];
node = info.node;
indexOfDowntimePeriods = info.indexOfDowntimePeriods;
lastRefundedPeriod = info.lastRefundedPeriod;
active = info.active;
}
@ -424,11 +461,11 @@ contract PolicyManager is Upgradeable {
/**
* @dev Get ArrangementInfo structure by delegatecall
**/
function delegateGetArrangementInfo(address _target, bytes20 _policyId, address _node)
function delegateGetArrangementInfo(address _target, bytes20 _policyId, uint256 _index)
internal returns (ArrangementInfo memory result)
{
bytes32 memoryAddress = delegateGetData(
_target, "getArrangementInfo(bytes20,address)", 2, bytes32(_policyId), bytes32(_node));
_target, "getArrangementInfo(bytes20,uint256)", 2, bytes32(_policyId), bytes32(_index));
assembly {
result := memoryAddress
}
@ -448,6 +485,7 @@ contract PolicyManager is Upgradeable {
function verifyState(address _testTarget) public onlyOwner {
require(address(delegateGet(_testTarget, "escrow()")) == address(escrow));
require(uint256(delegateGet(_testTarget, "secondsPerPeriod()")) == secondsPerPeriod);
Policy storage policy = policies[RESERVED_POLICY_ID];
Policy memory policyToCheck = delegateGetPolicy(_testTarget, RESERVED_POLICY_ID);
require(policyToCheck.client == policy.client &&
@ -457,17 +495,14 @@ contract PolicyManager is Upgradeable {
policyToCheck.lastPeriod == policy.lastPeriod &&
policyToCheck.disabled == policy.disabled);
ArrangementInfo storage arrangement = policy.arrangements[RESERVED_NODE];
require(uint256(delegateGet(_testTarget, "getArrangementsLength(bytes20)",
RESERVED_POLICY_ID)) == policy.arrangements.length);
ArrangementInfo storage arrangement = policy.arrangements[0];
ArrangementInfo memory arrangementToCheck = delegateGetArrangementInfo(
_testTarget, RESERVED_POLICY_ID, RESERVED_NODE);
require(arrangementToCheck.indexOfDowntimePeriods == arrangement.indexOfDowntimePeriods &&
arrangementToCheck.lastRefundedPeriod == arrangement.lastRefundedPeriod &&
arrangementToCheck.active == arrangement.active);
require(uint256(delegateGet(_testTarget, "getPolicyNodesLength(bytes20)",
RESERVED_POLICY_ID)) == policy.nodes.length);
require(address(delegateGet(_testTarget, "getPolicyNode(bytes20,uint256)",
RESERVED_POLICY_ID, 0)) == policy.nodes[0]);
_testTarget, RESERVED_POLICY_ID, 0);
require(arrangementToCheck.node == arrangement.node &&
arrangementToCheck.indexOfDowntimePeriods == arrangement.indexOfDowntimePeriods &&
arrangementToCheck.lastRefundedPeriod == arrangement.lastRefundedPeriod);
NodeInfo storage nodeInfo = nodes[RESERVED_NODE];
NodeInfo memory nodeInfoToCheck = delegateGetNodeInfo(_testTarget, RESERVED_NODE);
@ -483,6 +518,7 @@ contract PolicyManager is Upgradeable {
function finishUpgrade(address _target) public onlyOwner {
PolicyManager policyManager = PolicyManager(_target);
escrow = policyManager.escrow();
secondsPerPeriod = policyManager.secondsPerPeriod();
// Create fake Policy and NodeInfo to use them in verifyState(address)
Policy storage policy = policies[RESERVED_POLICY_ID];
policy.client = owner;
@ -491,10 +527,7 @@ contract PolicyManager is Upgradeable {
policy.rewardRate = 3;
policy.firstReward = 4;
policy.disabled = true;
policy.nodes.push(RESERVED_NODE);
policy.arrangements[RESERVED_NODE].indexOfDowntimePeriods = 11;
policy.arrangements[RESERVED_NODE].lastRefundedPeriod = 22;
policy.arrangements[RESERVED_NODE].active = true;
policy.arrangements.push(ArrangementInfo(RESERVED_NODE, 11, 22));
NodeInfo storage nodeInfo = nodes[RESERVED_NODE];
nodeInfo.reward = 100;
nodeInfo.rewardRate = 33;

View File

@ -32,17 +32,15 @@ contract IssuerMock is Issuer {
uint256 _period,
uint256 _lockedValue,
uint256 _totalLockedValue,
uint256 _allLockedPeriods,
uint256 _decimals
uint256 _allLockedPeriods
)
public returns (uint256 amount, uint256 decimals)
public returns (uint256 amount)
{
(amount, decimals) = mint(
amount = mint(
_period,
_lockedValue,
_totalLockedValue,
_allLockedPeriods,
_decimals);
_allLockedPeriods);
token.transfer(msg.sender, amount);
}

View File

@ -17,33 +17,16 @@ contract MinersEscrowForPolicyMock {
PolicyManager public policyManager;
uint256 public secondsPerPeriod;
mapping(address => bool) public nodes;
uint256 public lastActivePeriod;
Downtime[] public downtime;
/**
* @param _nodes Addresses of nodes that allow to use policy manager
* @param _hoursPerPeriod Size of period in hours
**/
constructor(address[] _nodes, uint256 _hoursPerPeriod) public {
for (uint256 i = 0; i < _nodes.length; i++) {
nodes[_nodes[i]] = true;
}
constructor(uint256 _hoursPerPeriod) public {
secondsPerPeriod = _hoursPerPeriod * 1 hours;
}
/**
* @notice Return non zero value for node
**/
function getLockedTokens(address _owner)
public view returns (uint256)
{
if (nodes[_owner]) {
return 1;
}
return 0;
}
/**
* @return Number of current period
**/
@ -102,11 +85,16 @@ contract MinersEscrowForPolicyMock {
}
/**
* @notice Emulate minerInfo
* @notice Emulate getLastActivePeriod
**/
function minerInfo(address)
public view returns (uint256, uint256, uint256, uint256, uint256 result)
{
result = lastActivePeriod;
function getLastActivePeriod(address) public view returns (uint256) {
return lastActivePeriod;
}
/**
* @notice Emulate node registration
**/
function register(address _node) external {
policyManager.register(_node, getCurrentPeriod() - 1);
}
}

View File

@ -16,6 +16,10 @@ contract PolicyManagerForMinersEscrowMock {
escrow = _escrow;
}
function register(address _node, uint256 _period) external {
nodes[_node].push(_period);
}
/**
* @notice Update node info
**/

View File

@ -128,7 +128,7 @@ contract ContractV1 is ContractInterface, Upgradeable {
require(uint(delegateGet(_testTarget, "storageValue()")) == storageValue);
bytes memory value = delegateGetBytes(_testTarget, "dynamicallySizedValue()");
require(value.length == bytes(dynamicallySizedValue).length &&
keccak256(bytes(value)) == keccak256(bytes(dynamicallySizedValue)));
keccak256(value) == keccak256(bytes(dynamicallySizedValue)));
require(uint(delegateGet(_testTarget, "getArrayValueLength()")) == arrayValues.length);
for (uint i = 0; i < arrayValues.length; i++) {

View File

@ -137,7 +137,7 @@ contract ContractV2 is ContractInterface, Upgradeable {
require(uint(delegateGet(_testTarget, "storageValue()")) == storageValue);
bytes memory value = delegateGetBytes(_testTarget, "dynamicallySizedValue()");
require(value.length == bytes(dynamicallySizedValue).length &&
keccak256(bytes(value)) == keccak256(bytes(dynamicallySizedValue)));
keccak256(value) == keccak256(bytes(dynamicallySizedValue)));
require(uint(delegateGet(_testTarget, "getArrayValueLength()")) == arrayValues.length);
for (uint i = 0; i < arrayValues.length; i++) {

View File

@ -37,7 +37,7 @@ def test_dispatcher(web3, chain):
# Only owner can change target address for dispatcher
with pytest.raises((TransactionFailed, ValueError)):
tx = dispatcher.functions.upgrade(contract2_lib.address).transact({'from': account})
tx = dispatcher.functions.upgrade(contract2_lib.address).transact({'from': account})
chain.wait_for_receipt(tx)
assert dispatcher.functions.target().call() == contract1_lib.address

View File

@ -40,23 +40,23 @@ def test_issuer(web3, chain, token):
chain.wait_for_receipt(tx)
# Mint some tokens
tx = issuer.functions.testMint(0, 1000, 2000, 0, 0).transact({'from': ursula})
tx = issuer.functions.testMint(0, 1000, 2000, 0).transact({'from': ursula})
chain.wait_for_receipt(tx)
assert 10 == token.functions.balanceOf(ursula).call()
assert balance - 10 == token.functions.balanceOf(issuer.address).call()
# Mint more tokens
tx = issuer.functions.testMint(0, 500, 500, 0, 0).transact({'from': ursula})
tx = issuer.functions.testMint(0, 500, 500, 0).transact({'from': ursula})
chain.wait_for_receipt(tx)
assert 30 == token.functions.balanceOf(ursula).call()
assert balance - 30 == token.functions.balanceOf(issuer.address).call()
tx = issuer.functions.testMint(0, 500, 500, 10 ** 7, 0).transact({'from': ursula})
tx = issuer.functions.testMint(0, 500, 500, 10 ** 7).transact({'from': ursula})
chain.wait_for_receipt(tx)
assert 70 == token.functions.balanceOf(ursula).call()
assert balance - 70 == token.functions.balanceOf(issuer.address).call()
tx = issuer.functions.testMint(0, 500, 500, 2 * 10 ** 7, 0).transact({'from': ursula})
tx = issuer.functions.testMint(0, 500, 500, 2 * 10 ** 7).transact({'from': ursula})
chain.wait_for_receipt(tx)
assert 110 == token.functions.balanceOf(ursula).call()
assert balance - 110 == token.functions.balanceOf(issuer.address).call()
@ -80,28 +80,28 @@ def test_inflation_rate(web3, chain, token):
# Mint some tokens
period = issuer.functions.getCurrentPeriod().call()
tx = issuer.functions.testMint(period + 1, 1, 1, 0, 0).transact({'from': ursula})
tx = issuer.functions.testMint(period + 1, 1, 1, 0).transact({'from': ursula})
chain.wait_for_receipt(tx)
one_period = token.functions.balanceOf(ursula).call()
# Mint more tokens in the same period
tx = issuer.functions.testMint(period + 1, 1, 1, 0, 0).transact({'from': ursula})
tx = issuer.functions.testMint(period + 1, 1, 1, 0).transact({'from': ursula})
chain.wait_for_receipt(tx)
assert 2 * one_period == token.functions.balanceOf(ursula).call()
# Mint tokens in the next period
tx = issuer.functions.testMint(period + 2, 1, 1, 0, 0).transact({'from': ursula})
tx = issuer.functions.testMint(period + 2, 1, 1, 0).transact({'from': ursula})
chain.wait_for_receipt(tx)
assert 3 * one_period > token.functions.balanceOf(ursula).call()
minted_amount = token.functions.balanceOf(ursula).call() - 2 * one_period
# Mint tokens in the next period
tx = issuer.functions.testMint(period + 1, 1, 1, 0, 0).transact({'from': ursula})
tx = issuer.functions.testMint(period + 1, 1, 1, 0).transact({'from': ursula})
chain.wait_for_receipt(tx)
assert 2 * one_period + 2 * minted_amount == token.functions.balanceOf(ursula).call()
# Mint tokens in the next period
tx = issuer.functions.testMint(period + 3, 1, 1, 0, 0).transact({'from': ursula})
tx = issuer.functions.testMint(period + 3, 1, 1, 0).transact({'from': ursula})
chain.wait_for_receipt(tx)
assert 2 * one_period + 3 * minted_amount > token.functions.balanceOf(ursula).call()
@ -140,7 +140,7 @@ def test_verifying_state(web3, chain, token):
assert 2 == contract.functions.lockedPeriodsCoefficient().call()
assert 2 == contract.functions.awardedPeriods().call()
assert period == contract.functions.lastMintedPeriod().call()
assert 2 * 10 ** 40 == contract.functions.futureSupply().call()
assert 2 * 10 ** 40 == contract.functions.totalSupply().call()
tx = contract.functions.setValueToCheck(3).transact({'from': creator})
chain.wait_for_receipt(tx)
assert 3 == contract.functions.valueToCheck().call()
@ -163,7 +163,7 @@ def test_verifying_state(web3, chain, token):
assert 1 == contract.functions.lockedPeriodsCoefficient().call()
assert 1 == contract.functions.awardedPeriods().call()
assert period == contract.functions.lastMintedPeriod().call()
assert 2 * 10 ** 40 == contract.functions.futureSupply().call()
assert 2 * 10 ** 40 == contract.functions.totalSupply().call()
with pytest.raises((TransactionFailed, ValueError)):
tx = contract.functions.setValueToCheck(2).transact({'from': creator})
chain.wait_for_receipt(tx)

View File

@ -31,6 +31,13 @@ def escrow_contract(web3, chain, token, request):
abi=contract.abi,
address=dispatcher.address,
ContractFactoryClass=Contract)
policy_manager, _ = chain.interface.deploy_contract(
'PolicyManagerForMinersEscrowMock', token.address, contract.address
)
tx = contract.functions.setPolicyManager(policy_manager.address).transact()
chain.wait_for_receipt(tx)
assert policy_manager.address == contract.functions.policyManager().call()
return contract
return make_escrow
@ -48,12 +55,6 @@ def test_escrow(web3, chain, token, escrow_contract):
divides_log = escrow.events.Divided.createFilter(fromBlock='latest')
withdraw_log = escrow.events.Withdrawn.createFilter(fromBlock='latest')
policy_manager, _ = chain.interface.deploy_contract(
'PolicyManagerForMinersEscrowMock', token.address, escrow.address
)
tx = escrow.functions.setPolicyManager(policy_manager.address).transact()
chain.wait_for_receipt(tx)
# Give Ursula and Ursula(2) some coins
tx = token.functions.transfer(ursula1, 10000).transact({'from': creator})
chain.wait_for_receipt(tx)
@ -125,6 +126,7 @@ def test_escrow(web3, chain, token, escrow_contract):
assert 1000 == escrow.functions.getLockedTokens(ursula1, 1).call()
assert 1000 == escrow.functions.getLockedTokens(ursula1, 2).call()
assert 0 == escrow.functions.getLockedTokens(ursula1, 3).call()
assert escrow.functions.getCurrentPeriod().call() + 1 == escrow.functions.getLastActivePeriod(ursula1).call()
events = deposit_log.get_all_entries()
assert 1 == len(events)
@ -152,6 +154,7 @@ def test_escrow(web3, chain, token, escrow_contract):
assert 9500 == token.functions.balanceOf(ursula2).call()
assert 0 == escrow.functions.getLockedTokens(ursula2).call()
assert 500 == escrow.functions.getLockedTokens(ursula2, 1).call()
assert escrow.functions.getCurrentPeriod().call() + 1 == escrow.functions.getLastActivePeriod(ursula2).call()
events = deposit_log.get_all_entries()
assert 2 == len(events)
@ -189,6 +192,7 @@ def test_escrow(web3, chain, token, escrow_contract):
# Ursula can deposit more tokens
tx = escrow.functions.confirmActivity().transact({'from': ursula1})
chain.wait_for_receipt(tx)
assert escrow.functions.getCurrentPeriod().call() + 1 == escrow.functions.getLastActivePeriod(ursula1).call()
assert 1000 == escrow.functions.getLockedTokens(ursula1, 1).call()
assert 0 == escrow.functions.getLockedTokens(ursula1, 2).call()
events = activity_log.get_all_entries()
@ -310,13 +314,12 @@ def test_escrow(web3, chain, token, escrow_contract):
assert 500 == escrow.functions.getLockedTokens(ursula2, 2).call()
assert 0 == escrow.functions.getLockedTokens(ursula2, 3).call()
chain.time_travel(hours=1)
assert 1000 == escrow.functions.getLockedTokens(ursula2).call()
# And increases locked time
chain.time_travel(hours=1)
tx = escrow.functions.divideStake(500, escrow.functions.getCurrentPeriod().call() + 1, 200, 1).transact({'from': ursula2})
period = escrow.functions.getCurrentPeriod().call()
tx = escrow.functions.divideStake(500, period + 1, 200, 1).transact({'from': ursula2})
chain.wait_for_receipt(tx)
assert 500 == escrow.functions.getLockedTokens(ursula2).call()
assert 1000 == escrow.functions.getLockedTokens(ursula2).call()
assert 500 == escrow.functions.getLockedTokens(ursula2, 1).call()
assert 200 == escrow.functions.getLockedTokens(ursula2, 2).call()
assert 0 == escrow.functions.getLockedTokens(ursula2, 3).call()
@ -326,23 +329,26 @@ def test_escrow(web3, chain, token, escrow_contract):
event_args = events[7]['args']
assert ursula2 == event_args['owner']
assert 200 == event_args['value']
assert escrow.functions.getCurrentPeriod().call() - 1 == event_args['firstPeriod']
assert period == event_args['firstPeriod']
assert 2 == event_args['periods']
events = divides_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert ursula2 == event_args['owner']
assert 500 == event_args['oldValue']
assert 1 == event_args['periods']
assert period + 1 == event_args['lastPeriod']
assert 200 == event_args['newValue']
assert 1 == event_args['periods']
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
chain.wait_for_receipt(tx)
chain.time_travel(hours=1)
tx = escrow.functions.divideStake(300, escrow.functions.getCurrentPeriod().call() + 1, 200, 1).transact({'from': ursula2})
period = escrow.functions.getCurrentPeriod().call()
tx = escrow.functions.divideStake(300, period, 200, 2).transact({'from': ursula2})
chain.wait_for_receipt(tx)
assert 500 == escrow.functions.getLockedTokens(ursula2).call()
assert 500 == escrow.functions.getLockedTokens(ursula2, 1).call()
assert 400 == escrow.functions.getLockedTokens(ursula2, 2).call()
assert 400 == escrow.functions.getLockedTokens(ursula2, 1).call()
assert 200 == escrow.functions.getLockedTokens(ursula2, 2).call()
assert 0 == escrow.functions.getLockedTokens(ursula2, 3).call()
events = divides_log.get_all_entries()
@ -350,15 +356,15 @@ def test_escrow(web3, chain, token, escrow_contract):
event_args = events[1]['args']
assert ursula2 == event_args['owner']
assert 300 == event_args['oldValue']
assert 1 == event_args['periods']
assert period == event_args['lastPeriod']
assert 200 == event_args['newValue']
assert 1 == event_args['periods']
assert 2 == event_args['periods']
tx = escrow.functions.divideStake(200, escrow.functions.getCurrentPeriod().call() + 2, 100, 1).transact({'from': ursula2})
tx = escrow.functions.divideStake(200, period + 1, 100, 2).transact({'from': ursula2})
chain.wait_for_receipt(tx)
assert 500 == escrow.functions.getLockedTokens(ursula2).call()
assert 500 == escrow.functions.getLockedTokens(ursula2, 1).call()
assert 400 == escrow.functions.getLockedTokens(ursula2, 2).call()
assert 400 == escrow.functions.getLockedTokens(ursula2, 1).call()
assert 300 == escrow.functions.getLockedTokens(ursula2, 2).call()
assert 100 == escrow.functions.getLockedTokens(ursula2, 3).call()
assert 0 == escrow.functions.getLockedTokens(ursula2, 4).call()
@ -367,32 +373,54 @@ def test_escrow(web3, chain, token, escrow_contract):
event_args = events[2]['args']
assert ursula2 == event_args['owner']
assert 200 == event_args['oldValue']
assert 1 == event_args['periods']
assert period + 1 == event_args['lastPeriod']
assert 100 == event_args['newValue']
assert 2 == event_args['periods']
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
chain.wait_for_receipt(tx)
chain.time_travel(hours=1)
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
chain.wait_for_receipt(tx)
chain.time_travel(hours=1)
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
chain.wait_for_receipt(tx)
# Can't divide old stake
period = escrow.functions.getCurrentPeriod().call()
with pytest.raises((TransactionFailed, ValueError)):
tx = escrow.functions.divideStake(500, period - 3, 200, 10).transact({'from': ursula2})
chain.wait_for_receipt(tx)
tx = escrow.functions.divideStake(200, period, 100, 1).transact({'from': ursula2})
chain.wait_for_receipt(tx)
events = divides_log.get_all_entries()
assert 4 == len(events)
event_args = events[3]['args']
assert ursula2 == event_args['owner']
assert 200 == event_args['oldValue']
assert period == event_args['lastPeriod']
assert 100 == event_args['newValue']
assert 1 == event_args['periods']
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
chain.wait_for_receipt(tx)
chain.time_travel(hours=1)
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
chain.wait_for_receipt(tx)
chain.time_travel(hours=1)
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
chain.wait_for_receipt(tx)
events = activity_log.get_all_entries()
assert 12 == len(events)
event_args = events[10]['args']
assert 14 == len(events)
event_args = events[11]['args']
assert ursula2 == event_args['owner']
assert escrow.functions.getCurrentPeriod().call() == event_args['period']
assert 400 == event_args['value']
event_args = events[11]['args']
assert 300 == event_args['value']
event_args = events[12]['args']
assert ursula2 == event_args['owner']
assert escrow.functions.getCurrentPeriod().call() + 1 == event_args['period']
assert 100 == event_args['value']
event_args = events[13]['args']
assert ursula2 == event_args['owner']
assert escrow.functions.getCurrentPeriod().call() + 1 == event_args['period']
assert 100 == event_args['value']
assert 5 == len(deposit_log.get_all_entries())
assert 10 == len(lock_log.get_all_entries())
assert 11 == len(lock_log.get_all_entries())
assert 1 == len(withdraw_log.get_all_entries())
@ -481,6 +509,11 @@ def test_locked_distribution(web3, chain, token, escrow_contract):
@pytest.mark.slow
def test_mining(web3, chain, token, escrow_contract):
escrow = escrow_contract(1500)
policy_manager_interface = chain.interface.get_contract_factory('PolicyManagerForMinersEscrowMock')
policy_manager = web3.eth.contract(
abi=policy_manager_interface.abi,
address=escrow.functions.policyManager().call(),
ContractFactoryClass=Contract)
creator = web3.eth.accounts[0]
ursula1 = web3.eth.accounts[1]
ursula2 = web3.eth.accounts[2]
@ -498,13 +531,6 @@ def test_mining(web3, chain, token, escrow_contract):
tx = escrow.functions.initialize().transact({'from': creator})
chain.wait_for_receipt(tx)
policy_manager, _ = chain.interface.deploy_contract(
'PolicyManagerForMinersEscrowMock', token.address, escrow.address
)
tx = escrow.functions.setPolicyManager(policy_manager.address).transact({'from': creator})
chain.wait_for_receipt(tx)
assert policy_manager.address == escrow.functions.policyManager().call()
# Give Ursula and Ursula(2) some coins
tx = token.functions.transfer(ursula1, 10000).transact({'from': creator})
chain.wait_for_receipt(tx)
@ -530,9 +556,24 @@ def test_mining(web3, chain, token, escrow_contract):
chain.wait_for_receipt(tx)
tx = escrow.functions.deposit(500, 2).transact({'from': ursula2})
chain.wait_for_receipt(tx)
period = escrow.functions.getCurrentPeriod().call()
assert 1 == policy_manager.functions.getPeriodsLength(ursula1).call()
assert 1 == policy_manager.functions.getPeriodsLength(ursula2).call()
assert period == policy_manager.functions.getPeriod(ursula1, 0).call()
assert period == policy_manager.functions.getPeriod(ursula2, 0).call()
assert 1 == escrow.functions.getDowntimeLength(ursula1).call()
downtime = escrow.functions.getDowntime(ursula1, 0).call()
assert 1 == downtime[0]
assert period == downtime[1]
assert 1 == escrow.functions.getDowntimeLength(ursula2).call()
downtime = escrow.functions.getDowntime(ursula2, 0).call()
assert 1 == downtime[0]
assert period == downtime[1]
assert period + 1 == escrow.functions.getLastActivePeriod(ursula1).call()
assert period + 1 == escrow.functions.getLastActivePeriod(ursula2).call()
# Ursula divides her stake
tx = escrow.functions.divideStake(1000, escrow.functions.getCurrentPeriod().call() + 2, 500, 1).transact({'from': ursula1})
tx = escrow.functions.divideStake(1000, period + 2, 500, 1).transact({'from': ursula1})
chain.wait_for_receipt(tx)
# Using locked tokens starts from next period
@ -540,7 +581,7 @@ def test_mining(web3, chain, token, escrow_contract):
# Ursula can't use method from Issuer contract
with pytest.raises(Exception):
tx = escrow.functions.mint(1, 1, 1, 1, 1).transact({'from': ursula1})
tx = escrow.functions.mint(1, 1, 1, 1).transact({'from': ursula1})
chain.wait_for_receipt(tx)
# Only Ursula confirm next period
@ -548,6 +589,7 @@ def test_mining(web3, chain, token, escrow_contract):
assert 1500 == escrow.functions.getAllLockedTokens().call()
tx = escrow.functions.confirmActivity().transact({'from': ursula1})
chain.wait_for_receipt(tx)
assert 1 == escrow.functions.getDowntimeLength(ursula1).call()
# Checks that no error
tx = escrow.functions.confirmActivity().transact({'from': ursula1})
@ -561,8 +603,13 @@ def test_mining(web3, chain, token, escrow_contract):
chain.wait_for_receipt(tx)
tx = escrow.functions.mint().transact({'from': ursula2})
chain.wait_for_receipt(tx)
period = escrow.functions.getCurrentPeriod().call()
assert 1046 == escrow.functions.minerInfo(ursula1).call()[VALUE_FIELD]
assert 525 == escrow.functions.minerInfo(ursula2).call()[VALUE_FIELD]
assert 1 == escrow.functions.getDowntimeLength(ursula1).call()
assert 1 == escrow.functions.getDowntimeLength(ursula2).call()
assert period + 1 == escrow.functions.getLastActivePeriod(ursula1).call()
assert period - 1 == escrow.functions.getLastActivePeriod(ursula2).call()
events = mining_log.get_all_entries()
assert 2 == len(events)
@ -575,11 +622,11 @@ def test_mining(web3, chain, token, escrow_contract):
assert 25 == event_args['value']
assert escrow.functions.getCurrentPeriod().call() - 1 == event_args['period']
assert 1 == policy_manager.functions.getPeriodsLength(ursula1).call()
assert 1 == policy_manager.functions.getPeriodsLength(ursula2).call()
assert 2 == policy_manager.functions.getPeriodsLength(ursula1).call()
assert 2 == policy_manager.functions.getPeriodsLength(ursula2).call()
period = escrow.functions.getCurrentPeriod().call() - 1
assert period == policy_manager.functions.getPeriod(ursula1, 0).call()
assert period == policy_manager.functions.getPeriod(ursula2, 0).call()
assert period == policy_manager.functions.getPeriod(ursula1, 1).call()
assert period == policy_manager.functions.getPeriod(ursula2, 1).call()
# Ursula try to mint again
tx = escrow.functions.mint().transact({'from': ursula1})
@ -595,8 +642,15 @@ def test_mining(web3, chain, token, escrow_contract):
tx = escrow.functions.confirmActivity().transact({'from': ursula1})
chain.wait_for_receipt(tx)
# But Ursula(2) can
period = escrow.functions.getCurrentPeriod().call()
assert period - 2 == escrow.functions.getLastActivePeriod(ursula2).call()
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
chain.wait_for_receipt(tx)
assert period + 1 == escrow.functions.getLastActivePeriod(ursula2).call()
assert 2 == escrow.functions.getDowntimeLength(ursula2).call()
downtime = escrow.functions.getDowntime(ursula2, 1).call()
assert period - 1 == downtime[0]
assert period == downtime[1]
# Ursula mint tokens
chain.time_travel(hours=1)
@ -614,12 +668,12 @@ def test_mining(web3, chain, token, escrow_contract):
event_args = events[2]['args']
assert ursula1 == event_args['owner']
assert 106 == event_args['value']
assert escrow.functions.getCurrentPeriod().call() - 1 == event_args['period']
assert period == event_args['period']
assert 3 == policy_manager.functions.getPeriodsLength(ursula1).call()
assert 1 == policy_manager.functions.getPeriodsLength(ursula2).call()
assert period + 1 == policy_manager.functions.getPeriod(ursula1, 1).call()
assert period + 2 == policy_manager.functions.getPeriod(ursula1, 2).call()
assert 4 == policy_manager.functions.getPeriodsLength(ursula1).call()
assert 2 == policy_manager.functions.getPeriodsLength(ursula2).call()
assert period - 1 == policy_manager.functions.getPeriod(ursula1, 2).call()
assert period == policy_manager.functions.getPeriod(ursula1, 3).call()
# Ursula(2) mint tokens
chain.time_travel(hours=1)
@ -637,9 +691,9 @@ def test_mining(web3, chain, token, escrow_contract):
assert escrow.functions.getCurrentPeriod().call() - 1 == event_args['period']
period = escrow.functions.getCurrentPeriod().call() - 1
assert 3 == policy_manager.functions.getPeriodsLength(ursula1).call()
assert 2 == policy_manager.functions.getPeriodsLength(ursula2).call()
assert period == policy_manager.functions.getPeriod(ursula2, 1).call()
assert 4 == policy_manager.functions.getPeriodsLength(ursula1).call()
assert 3 == policy_manager.functions.getPeriodsLength(ursula2).call()
assert period == policy_manager.functions.getPeriod(ursula2, 2).call()
# Ursula(2) can't more confirm activity
with pytest.raises((TransactionFailed, ValueError)):
@ -649,6 +703,8 @@ def test_mining(web3, chain, token, escrow_contract):
# Ursula can't confirm and get reward because no locked tokens
tx = escrow.functions.mint().transact({'from': ursula1})
chain.wait_for_receipt(tx)
period = escrow.functions.getCurrentPeriod().call()
assert period - 2 == escrow.functions.getLastActivePeriod(ursula1).call()
assert 1152 == escrow.functions.minerInfo(ursula1).call()[VALUE_FIELD]
with pytest.raises((TransactionFailed, ValueError)):
tx = escrow.functions.confirmActivity().transact({'from': ursula1})
@ -659,17 +715,26 @@ def test_mining(web3, chain, token, escrow_contract):
chain.wait_for_receipt(tx)
tx = escrow.functions.lock(500, 2).transact({'from': ursula2})
chain.wait_for_receipt(tx)
assert 3 == escrow.functions.getDowntimeLength(ursula2).call()
downtime = escrow.functions.getDowntime(ursula2, 2).call()
assert period == downtime[0]
assert period == downtime[1]
# Ursula(2) mint only one period (by using deposit/approveAndCall function)
chain.time_travel(hours=5)
period = escrow.functions.getCurrentPeriod().call()
assert period - 4 == escrow.functions.getLastActivePeriod(ursula2).call()
tx = token.functions.approveAndCall(escrow.address, 100, web3.toBytes(2)).transact({'from': ursula2})
chain.wait_for_receipt(tx)
assert 1152 == escrow.functions.minerInfo(ursula1).call()[VALUE_FIELD]
assert 1025 == escrow.functions.minerInfo(ursula2).call()[VALUE_FIELD]
assert 4 == escrow.functions.getDowntimeLength(ursula2).call()
downtime = escrow.functions.getDowntime(ursula2, 3).call()
assert period - 3 == downtime[0]
assert period == downtime[1]
period = escrow.functions.getCurrentPeriod().call() - 4
assert 3 == policy_manager.functions.getPeriodsLength(ursula2).call()
assert period == policy_manager.functions.getPeriod(ursula2, 2).call()
assert 4 == policy_manager.functions.getPeriodsLength(ursula2).call()
assert period - 4 == policy_manager.functions.getPeriod(ursula2, 3).call()
events = mining_log.get_all_entries()
assert 5 == len(events)
@ -682,6 +747,7 @@ def test_mining(web3, chain, token, escrow_contract):
chain.time_travel(hours=1)
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
chain.wait_for_receipt(tx)
assert 4 == escrow.functions.getDowntimeLength(ursula2).call()
chain.time_travel(hours=1)
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
chain.wait_for_receipt(tx)
@ -709,6 +775,11 @@ def test_mining(web3, chain, token, escrow_contract):
@pytest.mark.slow
def test_pre_deposit(web3, chain, token, escrow_contract):
escrow = escrow_contract(1500)
policy_manager_interface = chain.interface.get_contract_factory('PolicyManagerForMinersEscrowMock')
policy_manager = web3.eth.contract(
abi=policy_manager_interface.abi,
address=escrow.functions.policyManager().call(),
ContractFactoryClass=Contract)
creator = web3.eth.accounts[0]
deposit_log = escrow.events.Deposited.createFilter(fromBlock='latest')
@ -730,6 +801,11 @@ def test_pre_deposit(web3, chain, token, escrow_contract):
assert 1000 == escrow.functions.getLockedTokens(owner, 1).call()
assert 1000 == escrow.functions.getLockedTokens(owner, 10).call()
assert 0 == escrow.functions.getLockedTokens(owner, 11).call()
period = escrow.functions.getCurrentPeriod().call()
assert 1 == policy_manager.functions.getPeriodsLength(owner).call()
assert period == policy_manager.functions.getPeriod(owner, 0).call()
assert 0 == escrow.functions.getDowntimeLength(owner).call()
assert 0 == escrow.functions.getLastActivePeriod(owner).call()
# Can't pre-deposit tokens again for same owner
with pytest.raises((TransactionFailed, ValueError)):
@ -756,11 +832,16 @@ def test_pre_deposit(web3, chain, token, escrow_contract):
chain.wait_for_receipt(tx)
assert 2500 == token.functions.balanceOf(escrow.address).call()
period = escrow.functions.getCurrentPeriod().call()
for index, owner in enumerate(owners):
assert 100 * (index + 1) == escrow.functions.minerInfo(owner).call()[VALUE_FIELD]
assert 100 * (index + 1) == escrow.functions.getLockedTokens(owner, 1).call()
assert 100 * (index + 1) == escrow.functions.getLockedTokens(owner, 50 * (index + 1)).call()
assert 0 == escrow.functions.getLockedTokens(owner, 50 * (index + 1) + 1).call()
assert 1 == policy_manager.functions.getPeriodsLength(owner).call()
assert period == policy_manager.functions.getPeriod(owner, 0).call()
assert 0 == escrow.functions.getDowntimeLength(owner).call()
assert 0 == escrow.functions.getLastActivePeriod(owner).call()
events = deposit_log.get_all_entries()
assert 6 == len(events)
@ -808,13 +889,13 @@ def test_miner_id(web3, chain, token, escrow_contract):
chain.wait_for_receipt(tx)
# Set miner ids
miner_id = os.urandom(32)
miner_id = os.urandom(33)
tx = escrow.functions.setMinerId(miner_id).transact({'from': miner})
chain.wait_for_receipt(tx)
assert 1 == escrow.functions.getMinerIdsLength(miner).call()
assert miner_id == escrow.functions.getMinerId(miner, 0).call()
miner_id = os.urandom(32)
miner_id = os.urandom(66)
tx = escrow.functions.setMinerId(miner_id).transact({'from': miner})
chain.wait_for_receipt(tx)
assert 2 == escrow.functions.getMinerIdsLength(miner).call()
@ -845,9 +926,10 @@ def test_verifying_state(web3, chain, token):
assert 1500 == contract.functions.maxAllowableLockedTokens().call()
# Initialize contract and miner
policy_manager, _ = chain.interface.deploy_contract('PolicyManagerForMinersEscrowMock', token.address, contract.address)
tx = contract.functions.setPolicyManager(policy_manager.address).transact({'from': creator})
policy_manager, _ = chain.interface.deploy_contract(
'PolicyManagerForMinersEscrowMock', token.address, contract.address
)
tx = contract.functions.setPolicyManager(policy_manager.address).transact()
chain.wait_for_receipt(tx)
tx = contract.functions.initialize().transact({'from': creator})
chain.wait_for_receipt(tx)
@ -858,6 +940,8 @@ def test_verifying_state(web3, chain, token):
chain.wait_for_receipt(tx)
tx = contract.functions.deposit(balance, 1000).transact({'from': miner})
chain.wait_for_receipt(tx)
tx = contract.functions.setMinerId(web3.toBytes(111)).transact({'from': miner})
chain.wait_for_receipt(tx)
# Upgrade to the second version
tx = dispatcher.functions.upgrade(contract_library_v2.address).transact({'from': creator})
@ -867,6 +951,8 @@ def test_verifying_state(web3, chain, token):
assert 1500 == contract.functions.maxAllowableLockedTokens().call()
assert policy_manager.address == contract.functions.policyManager().call()
assert 2 == contract.functions.valueToCheck().call()
assert 1 == web3.toInt(contract.functions.getMinerIdsLength(miner).call())
assert 111 == web3.toInt(contract.functions.getMinerId(miner, 0).call())
tx = contract.functions.setValueToCheck(3).transact({'from': creator})
chain.wait_for_receipt(tx)
assert 3 == contract.functions.valueToCheck().call()

View File

@ -4,6 +4,8 @@ import os
from web3.contract import Contract
NULL_ADDR = '0x' + '0' * 40
CLIENT_FIELD = 0
RATE_FIELD = 1
FIRST_REWARD_FIELD = 2
@ -18,19 +20,15 @@ MIN_REWARD_RATE_FIELD = 3
@pytest.fixture()
def escrow(web3, chain):
node1 = web3.eth.accounts[3]
node2 = web3.eth.accounts[4]
node3 = web3.eth.accounts[5]
def escrow(chain):
# Creator deploys the escrow
escrow, _ = chain.interface.deploy_contract('MinersEscrowForPolicyMock', [node1, node2, node3], 1)
escrow, _ = chain.interface.deploy_contract('MinersEscrowForPolicyMock', 1)
return escrow
@pytest.fixture(params=[False, True])
def policy_manager(web3, chain, escrow, request):
creator = web3.eth.accounts[0]
client = web3.eth.accounts[1]
creator, client, bad_node, node1, node2, node3, *everyone_else = web3.eth.accounts
# Creator deploys the policy manager
contract, _ = chain.interface.deploy_contract('PolicyManager', escrow.address)
@ -51,6 +49,14 @@ def policy_manager(web3, chain, escrow, request):
tx = escrow.functions.setPolicyManager(contract.address).transact({'from': creator})
chain.wait_for_receipt(tx)
# Register nodes
tx = escrow.functions.register(node1).transact()
chain.wait_for_receipt(tx)
tx = escrow.functions.register(node2).transact()
chain.wait_for_receipt(tx)
tx = escrow.functions.register(node3).transact()
chain.wait_for_receipt(tx)
return contract
@ -73,11 +79,16 @@ def test_create_revoke(web3, chain, escrow, policy_manager):
arrangement_refund_log = policy_manager.events.RefundForArrangement.createFilter(fromBlock='latest')
policy_refund_log = policy_manager.events.RefundForPolicy.createFilter(fromBlock='latest')
# Check registered nodes
assert 0 < policy_manager.functions.nodes(node1).call()[LAST_MINED_PERIOD_FIELD]
assert 0 < policy_manager.functions.nodes(node2).call()[LAST_MINED_PERIOD_FIELD]
assert 0 < policy_manager.functions.nodes(node3).call()[LAST_MINED_PERIOD_FIELD]
assert 0 == policy_manager.functions.nodes(bad_node).call()[LAST_MINED_PERIOD_FIELD]
# Try create policy for bad node
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.createPolicy(policy_id, 1, 0, [bad_node]).transact({'from': client, 'value': value})
chain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.createPolicy(policy_id, 1, 0, [node1, bad_node]).transact({'from': client, 'value': value})
chain.wait_for_receipt(tx)
@ -101,8 +112,8 @@ def test_create_revoke(web3, chain, escrow, policy_manager):
assert period + 1 == policy[START_PERIOD_FIELD]
assert period + 10 == policy[LAST_PERIOD_FIELD]
assert not policy[DISABLED_FIELD]
assert 1 == policy_manager.functions.getPolicyNodesLength(policy_id).call()
assert node1 == policy_manager.functions.getPolicyNode(policy_id, 0).call()
assert 1 == policy_manager.functions.getArrangementsLength(policy_id).call()
assert node1 == policy_manager.functions.getArrangementInfo(policy_id, 0).call()[0]
events = policy_created_log.get_all_entries()
assert 1 == len(events)
@ -172,6 +183,9 @@ def test_create_revoke(web3, chain, escrow, policy_manager):
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.revokeArrangement(policy_id_2, web3.eth.accounts[6]).transact({'from': client})
chain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.revokeArrangement(policy_id_2, NULL_ADDR).transact({'from': client})
chain.wait_for_receipt(tx)
tx = policy_manager.functions.revokeArrangement(policy_id_2, node1).transact({'from': client, 'gas_price': 0})
chain.wait_for_receipt(tx)
@ -191,6 +205,9 @@ def test_create_revoke(web3, chain, escrow, policy_manager):
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.revokeArrangement(policy_id_2, node1).transact({'from': client})
chain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.revokeArrangement(policy_id_2, NULL_ADDR).transact({'from': client})
chain.wait_for_receipt(tx)
tx = policy_manager.functions.revokePolicy(policy_id_2).transact({'from': client, 'gas_price': 0})
chain.wait_for_receipt(tx)
@ -351,6 +368,10 @@ def test_reward(web3, chain, escrow, policy_manager):
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.updateReward(node1, period + 1).transact({'from': node1})
chain.wait_for_receipt(tx)
# Can't register directly
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.register(bad_node, period).transact({'from': bad_node})
chain.wait_for_receipt(tx)
# Mint some periods
tx = escrow.functions.mint(period, 5).transact({'from': node1, 'gas_price': 0})
@ -435,6 +456,15 @@ def test_refund(web3, chain, escrow, policy_manager):
# Wait and refund all
chain.time_travel(hours=9)
# Check that methods only calculates value
tx = policy_manager.functions.calculateRefundValue(policy_id).transact({'from': client, 'gas_price': 0})
chain.wait_for_receipt(tx)
tx = policy_manager.functions.calculateRefundValue(policy_id, node1).transact({'from': client, 'gas_price': 0})
chain.wait_for_receipt(tx)
assert 210 == web3.eth.getBalance(policy_manager.address)
assert client_balance - 210 == web3.eth.getBalance(client)
assert 190 == policy_manager.functions.calculateRefundValue(policy_id, node1).call({'from': client})
assert 190 == policy_manager.functions.calculateRefundValue(policy_id).call({'from': client})
tx = policy_manager.functions.refund(policy_id).transact({'from': client, 'gas_price': 0})
chain.wait_for_receipt(tx)
assert 20 == web3.eth.getBalance(policy_manager.address)
@ -457,6 +487,8 @@ def test_refund(web3, chain, escrow, policy_manager):
assert 190 == event_args['value']
chain.time_travel(hours=1)
assert 20 == policy_manager.functions.calculateRefundValue(policy_id).call({'from': client})
assert 20 == policy_manager.functions.calculateRefundValue(policy_id, node1).call({'from': client})
tx = policy_manager.functions.refund(policy_id).transact({'from': client, 'gas_price': 0})
chain.wait_for_receipt(tx)
assert 0 == web3.eth.getBalance(policy_manager.address)
@ -464,16 +496,20 @@ def test_refund(web3, chain, escrow, policy_manager):
assert policy_manager.functions.policies(policy_id).call()[DISABLED_FIELD]
events = arrangement_refund_log.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert 1 == len(events)
events = arrangement_revoked_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert policy_id == event_args['policyId']
assert client == event_args['client']
assert node1 == event_args['node']
assert 20 == event_args['value']
events = policy_refund_log.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert 1 == len(events)
events = policy_revoked_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert policy_id == event_args['policyId']
assert client == event_args['client']
assert 20 == event_args['value']
@ -485,6 +521,15 @@ def test_refund(web3, chain, escrow, policy_manager):
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.refund(policy_id, node1).transact({'from': client})
chain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.refund(policy_id, NULL_ADDR).transact({'from': client})
chain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
policy_manager.functions.calculateRefundValue(policy_id).call({'from': client})
with pytest.raises((TransactionFailed, ValueError)):
policy_manager.functions.calculateRefundValue(policy_id, node1).call({'from': client})
with pytest.raises((TransactionFailed, ValueError)):
policy_manager.functions.calculateRefundValue(policy_id, NULL_ADDR).call({'from': client})
# Create policy again
chain.time_travel(hours=1)
@ -496,6 +541,8 @@ def test_refund(web3, chain, escrow, policy_manager):
chain.wait_for_receipt(tx)
# Nothing to refund
assert 0 == policy_manager.functions.calculateRefundValue(policy_id_2).call({'from': client})
assert 0 == policy_manager.functions.calculateRefundValue(policy_id_2, node1).call({'from': client})
tx = policy_manager.functions.refund(policy_id_2).transact({'from': client, 'gas_price': 0})
chain.wait_for_receipt(tx)
tx = policy_manager.functions.refund(policy_id_2, node1).transact({'from': client, 'gas_price': 0})
@ -504,34 +551,34 @@ def test_refund(web3, chain, escrow, policy_manager):
assert client_balance - int(3 * value + 1.5 * rate) == web3.eth.getBalance(client)
events = arrangement_refund_log.get_all_entries()
assert 6 == len(events)
event_args = events[2]['args']
assert 5 == len(events)
event_args = events[1]['args']
assert policy_id_2 == event_args['policyId']
assert client == event_args['client']
assert node1 == event_args['node']
assert 0 == event_args['value']
event_args = events[3]['args']
event_args = events[2]['args']
assert policy_id_2 == event_args['policyId']
assert client == event_args['client']
assert node2 == event_args['node']
assert 0 == event_args['value']
event_args = events[4]['args']
event_args = events[3]['args']
assert policy_id_2 == event_args['policyId']
assert client == event_args['client']
assert node3 == event_args['node']
assert 0 == event_args['value']
event_args = events[5]['args']
event_args = events[4]['args']
assert policy_id_2 == event_args['policyId']
assert client == event_args['client']
assert node1 == event_args['node']
assert 0 == event_args['value']
events = policy_refund_log.get_all_entries()
assert 3 == len(events)
event_args = events[2]['args']
events = policy_refund_log.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert policy_id_2 == event_args['policyId']
assert client == event_args['client']
assert 0 == event_args['value']
@ -540,10 +587,14 @@ def test_refund(web3, chain, escrow, policy_manager):
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.refund(policy_id_3).transact({'from': client})
chain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
policy_manager.functions.calculateRefundValue(policy_id_3).call({'from': client})
# Node try to refund by node
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.refund(policy_id_2).transact({'from': node1})
chain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
policy_manager.functions.calculateRefundValue(policy_id_2).call({'from': node1})
# Mint some periods and mark others as downtime periods
tx = escrow.functions.mint(period, 1).transact({'from': node1})
@ -565,6 +616,8 @@ def test_refund(web3, chain, escrow, policy_manager):
# Wait and refund
chain.time_travel(hours=10)
assert 360 == policy_manager.functions.calculateRefundValue(policy_id_2).call({'from': client})
assert 120 == policy_manager.functions.calculateRefundValue(policy_id_2, node1).call({'from': client})
tx = policy_manager.functions.refund(policy_id_2, node1).transact({'from': client, 'gas_price': 0})
chain.wait_for_receipt(tx)
assert 2 * value + 90 + rate == web3.eth.getBalance(policy_manager.address)
@ -572,22 +625,34 @@ def test_refund(web3, chain, escrow, policy_manager):
assert not policy_manager.functions.policies(policy_id_2).call()[DISABLED_FIELD]
events = arrangement_refund_log.get_all_entries()
assert 7 == len(events)
event_args = events[6]['args']
assert 5 == len(events)
events = arrangement_revoked_log.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert policy_id_2 == event_args['policyId']
assert client == event_args['client']
assert node1 == event_args['node']
assert 120 == event_args['value']
events = policy_refund_log.get_all_entries()
assert 3 == len(events)
assert 2 == len(events)
# Can't refund arrangement again
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.refund(policy_id, node1).transact({'from': client})
tx = policy_manager.functions.refund(policy_id_2, node1).transact({'from': client})
chain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.refund(policy_id_2, NULL_ADDR).transact({'from': client})
chain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
policy_manager.functions.calculateRefundValue(policy_id_2, node1).call({'from': client})
with pytest.raises((TransactionFailed, ValueError)):
policy_manager.functions.calculateRefundValue(policy_id_2, NULL_ADDR).call({'from': client})
# But can refund others
assert 240 == policy_manager.functions.calculateRefundValue(policy_id_2).call({'from': client})
assert 120 == policy_manager.functions.calculateRefundValue(policy_id_2, node2).call({'from': client})
assert 120 == policy_manager.functions.calculateRefundValue(policy_id_2, node3).call({'from': client})
tx = policy_manager.functions.refund(policy_id_2).transact({'from': client, 'gas_price': 0})
chain.wait_for_receipt(tx)
assert 3 * 90 == web3.eth.getBalance(policy_manager.address)
@ -595,22 +660,26 @@ def test_refund(web3, chain, escrow, policy_manager):
assert policy_manager.functions.policies(policy_id_2).call()[DISABLED_FIELD]
events = arrangement_refund_log.get_all_entries()
assert 9 == len(events)
event_args = events[7]['args']
assert 5 == len(events)
events = arrangement_revoked_log.get_all_entries()
assert 4 == len(events)
event_args = events[2]['args']
assert policy_id_2 == event_args['policyId']
assert client == event_args['client']
assert node2 == event_args['node']
assert 120 == event_args['value']
event_args = events[8]['args']
event_args = events[3]['args']
assert policy_id_2 == event_args['policyId']
assert client == event_args['client']
assert node3 == event_args['node']
assert 120 == event_args['value']
events = policy_refund_log.get_all_entries()
assert 4 == len(events)
event_args = events[3]['args']
assert 2 == len(events)
events = policy_revoked_log.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert policy_id_2 == event_args['policyId']
assert client == event_args['client']
assert 2 * 120 == event_args['value']
@ -635,6 +704,8 @@ def test_refund(web3, chain, escrow, policy_manager):
# Client revokes policy
chain.time_travel(hours=4)
assert 30 == policy_manager.functions.calculateRefundValue(policy_id_3).call({'from': client})
assert 30 == policy_manager.functions.calculateRefundValue(policy_id_3, node1).call({'from': client})
tx = policy_manager.functions.revokePolicy(policy_id_3).transact({'from': client, 'gas_price': 0})
chain.wait_for_receipt(tx)
assert 60 + 3 * 90 == web3.eth.getBalance(policy_manager.address)
@ -642,21 +713,21 @@ def test_refund(web3, chain, escrow, policy_manager):
assert policy_manager.functions.policies(policy_id_3).call()[DISABLED_FIELD]
events = arrangement_refund_log.get_all_entries()
assert 9 == len(events)
assert 5 == len(events)
events = policy_refund_log.get_all_entries()
assert 4 == len(events)
assert 2 == len(events)
events = arrangement_revoked_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert 5 == len(events)
event_args = events[4]['args']
assert policy_id_3 == event_args['policyId']
assert client == event_args['client']
assert node1 == event_args['node']
assert 150 == event_args['value']
events = policy_revoked_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert 3 == len(events)
event_args = events[2]['args']
assert policy_id_3 == event_args['policyId']
assert client == event_args['client']
assert 150 == event_args['value']
@ -675,10 +746,12 @@ def test_refund(web3, chain, escrow, policy_manager):
@pytest.mark.slow
def test_verifying_state(web3, chain):
creator = web3.eth.accounts[0]
address1 = web3.eth.accounts[1]
address2 = web3.eth.accounts[2]
# Deploy contract
# Deploy contracts
escrow1, _ = chain.interface.deploy_contract('MinersEscrowForPolicyMock', 1)
escrow2, _ = chain.interface.deploy_contract('MinersEscrowForPolicyMock', 1)
address1 = escrow1.address
address2 = escrow2.address
contract_library_v1, _ = chain.interface.deploy_contract('PolicyManager', address1)
dispatcher, _ = chain.interface.deploy_contract('Dispatcher', contract_library_v1.address)

View File

@ -14,6 +14,7 @@ class TestMiner:
miner = Miner(miner_agent=mock_miner_agent, address=ursula)
return miner
@pytest.mark.usefixtures("mock_policy_agent")
def test_miner_locking_tokens(self, chain, miner, mock_miner_agent):
assert mock_miner_agent.min_allowed_locked < miner.token_balance(), "Insufficient miner balance"

View File

@ -6,6 +6,7 @@ M = 10 ** 6
@pytest.mark.slow()
@pytest.mark.usefixtures("mock_policy_agent")
def test_get_swarm(chain, mock_token_agent, mock_miner_agent):
mock_token_agent.token_airdrop(amount=100000 * mock_token_agent.M)