mirror of https://github.com/nucypher/nucypher.git
commit
0fae344116
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@ contract PolicyManagerForMinersEscrowMock {
|
|||
escrow = _escrow;
|
||||
}
|
||||
|
||||
function register(address _node, uint256 _period) external {
|
||||
nodes[_node].push(_period);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Update node info
|
||||
**/
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue