diff --git a/nkms_eth/project/contracts/MinersEscrow.sol b/nkms_eth/project/contracts/MinersEscrow.sol index 08264ef22..3a478624e 100644 --- a/nkms_eth/project/contracts/MinersEscrow.sol +++ b/nkms_eth/project/contracts/MinersEscrow.sol @@ -233,7 +233,7 @@ contract MinersEscrow is Issuer, Ownable { internal view returns (uint256) { MinerInfo storage info = minerInfo[_owner]; - return _lockedTokens.divCeil(info.releaseRate).sub(1); + return _lockedTokens.divCeil(info.releaseRate).sub(uint(1)); } /** @@ -447,7 +447,7 @@ contract MinersEscrow is Issuer, Ownable { * @notice Mint tokens for sender for previous periods if he locked his tokens and confirmed activity **/ function mint() external onlyTokenOwner { - uint256 previousPeriod = getCurrentPeriod().sub(1); + uint256 previousPeriod = getCurrentPeriod().sub(uint(1)); MinerInfo storage info = minerInfo[msg.sender]; uint256 numberPeriodsForMinting = info.numberConfirmedPeriods; require(numberPeriodsForMinting > 0 && diff --git a/nkms_eth/project/contracts/PolicyManager.sol b/nkms_eth/project/contracts/PolicyManager.sol index eb60b6065..785dbda27 100644 --- a/nkms_eth/project/contracts/PolicyManager.sol +++ b/nkms_eth/project/contracts/PolicyManager.sol @@ -4,6 +4,7 @@ pragma solidity ^0.4.18; import "./zeppelin/token/ERC20/SafeERC20.sol"; import "./zeppelin/math/SafeMath.sol"; import "./zeppelin/math/Math.sol"; +import "./lib/AdditionalMath.sol"; import "./MinersEscrow.sol"; import "./NuCypherKMSToken.sol"; import "./proxy/Upgradeable.sol"; @@ -15,38 +16,61 @@ import "./proxy/Upgradeable.sol"; contract PolicyManager is Upgradeable { using SafeERC20 for NuCypherKMSToken; using SafeMath for uint256; + using AdditionalMath for uint256; + using AdditionalMath for int256; event PolicyCreated( bytes20 indexed policyId, address indexed client, - address indexed node + address[] indexed nodes + ); + event PolicyRevoked( + bytes20 indexed policyId, + address indexed client, + uint256 value + ); + event ArrangementRevoked( + bytes20 indexed policyId, + address indexed client, + address indexed node, + uint256 value ); - event PolicyRevoked(bytes20 indexed policyId); event Withdrawn(address indexed node, uint256 value); - event Refunded( + event RefundForArrangement( + bytes20 indexed policyId, + address indexed client, + address indexed node, + uint256 value + ); + event RefundForPolicy( bytes20 indexed policyId, address indexed client, uint256 value ); -// enum PolicyState { Pending, Active } -// enum PolicyType { Periods } + struct ArrangementInfo { + uint256 indexOfDowntimePeriods; + uint256 lastRefundedPeriod; + bool disabled; + } struct Policy { address client; - address node; -// PolicyState state; + mapping(address => ArrangementInfo) arrangements; + address[] nodes; // policy for activity periods uint256 rate; uint256 startPeriod; uint256 lastPeriod; - uint256 indexOfDowntimePeriods; + bool disabled; } struct NodeInfo { uint256 reward; - mapping (uint256 => uint256) rewardByPeriod; + uint256 lastRewardByPeriod; + uint256 lastMinedPeriod; + mapping (uint256 => int256) rewardChanges; } bytes20 constant RESERVED_POLICY_ID = bytes20(0); @@ -73,53 +97,51 @@ contract PolicyManager is Upgradeable { * @notice Create policy by client * @dev Generate policy id before creation * @param _policyId Policy id - * @param _node Node that will handle policy * @param _numberOfPeriods Duration of the policy in periods + * @param _nodes Nodes that will handle policy **/ function createPolicy( bytes20 _policyId, - address _node, - uint256 _numberOfPeriods + uint256 _numberOfPeriods, + address[] _nodes ) public payable { require( policies[_policyId].rate == 0 && - _node != RESERVED_NODE && _numberOfPeriods != 0 && - escrow.getLockedTokens(_node) != 0 && msg.value > 0 && - msg.value % _numberOfPeriods == 0 && + msg.value % _numberOfPeriods % _nodes.length == 0 && _policyId != RESERVED_POLICY_ID ); Policy storage policy = policies[_policyId]; policy.client = msg.sender; - policy.node = _node; -// policy.state = PolicyState.Pending; + policy.nodes = _nodes; uint256 currentPeriod = escrow.getCurrentPeriod(); - policy.startPeriod = currentPeriod.add(1); + policy.startPeriod = currentPeriod.add(uint(1)); policy.lastPeriod = currentPeriod.add(_numberOfPeriods); - uint256 feeByPeriod = msg.value.div(_numberOfPeriods); + uint256 feeByPeriod = msg.value.div(_numberOfPeriods).div(_nodes.length); policy.rate = feeByPeriod; + uint256 endPeriod = policy.lastPeriod.add(uint(1)); - NodeInfo storage node = nodes[_node]; - for (uint256 i = policy.startPeriod; i <= policy.lastPeriod; i++) { - node.rewardByPeriod[i] = node.rewardByPeriod[i].add(feeByPeriod); + policy.nodes = _nodes; + for (uint256 i = 0; i < _nodes.length; i++) { + require(escrow.getLockedTokens(_nodes[i]) != 0 && + _nodes[i] != RESERVED_NODE); + NodeInfo storage node = nodes[_nodes[i]]; + node.rewardChanges[policy.startPeriod] = node.rewardChanges[policy.startPeriod] + .add(feeByPeriod); + node.rewardChanges[endPeriod] = node.rewardChanges[endPeriod].sub(feeByPeriod); + // TODO node should pay for this + if (node.lastMinedPeriod == 0) { + node.lastMinedPeriod = currentPeriod; + } + ArrangementInfo storage arrangement = policy.arrangements[_nodes[i]]; + arrangement.indexOfDowntimePeriods = escrow.getDowntimePeriodsLength(_nodes[i]); } - policy.indexOfDowntimePeriods = escrow.getDowntimePeriodsLength(_node); - PolicyCreated(_policyId, msg.sender, _node); - } -// /** -// * @notice Confirm policy by node -// * @param _policyId Policy id -// **/ -// function confirmPolicy(bytes20 _policyId) public { -// Policy policy = policies[_policyId]; -// require(policy.state == PolicyState.Pending && -// policy.node == msg.sender); -// policy.state = PolicyState.Active; -// } + PolicyCreated(_policyId, msg.sender, _nodes); + } /** * @notice Update node reward @@ -129,8 +151,15 @@ contract PolicyManager is Upgradeable { function updateReward(address _node, uint256 _period) external { require(msg.sender == address(escrow)); NodeInfo storage node = nodes[_node]; - node.reward = node.reward.add(node.rewardByPeriod[_period]); - delete node.rewardByPeriod[_period]; + if (node.lastMinedPeriod == 0) { + return; + } + for (uint256 i = node.lastMinedPeriod + 1; i <= _period; i++) { + node.lastRewardByPeriod = node.lastRewardByPeriod.add(node.rewardChanges[i]); +// delete node.rewardChanges[i]; + } + node.lastMinedPeriod = _period; + node.reward = node.reward.add(node.lastRewardByPeriod); } /** @@ -151,19 +180,63 @@ contract PolicyManager is Upgradeable { **/ function revokePolicy(bytes20 _policyId) public { Policy storage policy = policies[_policyId]; - require(policy.client == msg.sender); - uint256 refund = calculateRefund(_policyId); - NodeInfo storage node = nodes[policy.node]; - for (uint256 i = policy.startPeriod; i <= policy.lastPeriod; i++) { - node.rewardByPeriod[i] = node.rewardByPeriod[i].sub(policy.rate); - refund = refund.add(policy.rate); + 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].disabled) { + continue; + } + uint256 nodeRefundValue = revokeArrangement(policy, node, endPeriod); + refundValue = refundValue.add(nodeRefundValue); + ArrangementRevoked(_policyId, msg.sender, node, nodeRefundValue); } - delete policies[_policyId]; - if (refund > 0) { - msg.sender.transfer(refund); - Refunded(_policyId, msg.sender, refund); + policy.disabled = true; + if (refundValue > 0) { + msg.sender.transfer(refundValue); } - PolicyRevoked(_policyId); + PolicyRevoked(_policyId, msg.sender, refundValue); + } + + /** + * @notice Revoke arrangement by client + * @param _policyId Policy id + * @param _node Node that will be excluded + **/ + 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].disabled); + uint256 endPeriod = policy.lastPeriod.add(uint(1)); + refundValue = revokeArrangement(policy, _node, endPeriod); + if (refundValue > 0) { + msg.sender.transfer(refundValue); + } + 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.rewardChanges[arrangement.lastRefundedPeriod] = + node.rewardChanges[arrangement.lastRefundedPeriod].sub(_policy.rate); + node.rewardChanges[_endPeriod] = node.rewardChanges[_endPeriod].add(_policy.rate); + refundValue = refundValue.add( + _endPeriod.sub(arrangement.lastRefundedPeriod).mul(_policy.rate)); + _policy.arrangements[_node].disabled = true; } /** @@ -172,79 +245,139 @@ contract PolicyManager is Upgradeable { **/ function refund(bytes20 _policyId) public { Policy storage policy = policies[_policyId]; -// require(policy.state == PolicyState.Active && -// msg.sender == policy.client); - require(msg.sender == policy.client); - - uint256 refundValue = calculateRefund(_policyId); - address client = policy.client; - if (policy.startPeriod > policy.lastPeriod) { - delete 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].disabled) { + numberOfActive--; + continue; + } + uint256 nodeRefundValue = calculateRefund(policy, node); + if (policy.arrangements[node].lastRefundedPeriod > policy.lastPeriod) { + policy.arrangements[node].disabled = true; + numberOfActive--; + } + refundValue = refundValue.add(nodeRefundValue); + RefundForArrangement(_policyId, msg.sender, node, nodeRefundValue); } if (refundValue > 0) { - client.transfer(refundValue); - Refunded(_policyId, client, refundValue); + msg.sender.transfer(refundValue); } + if (numberOfActive == 0) { + policy.disabled = true; + } + RefundForPolicy(_policyId, msg.sender, refundValue); + } + + /** + * @notice Refund part of one node's fee by client + * @param _policyId Policy id + * @param _node Node address + **/ + 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].disabled); + refundValue = calculateRefund(policy, _node); + if (policy.arrangements[_node].lastRefundedPeriod > policy.lastPeriod) { + policy.arrangements[_node].disabled = true; + } + if (refundValue > 0) { + msg.sender.transfer(refundValue); + } + RefundForArrangement(_policyId, msg.sender, _node, refundValue); } /** * @notice Calculate amount of refund - * @param _policyId Policy id + * @param _policy Policy + * @param _node Node for calculation **/ //TODO extract checkRefund method - function calculateRefund(bytes20 _policyId) internal returns (uint256) { - Policy storage policy = policies[_policyId]; - uint256 currentPeriod = escrow.getCurrentPeriod(); - uint256 maxPeriod = Math.min256(currentPeriod, policy.lastPeriod); - uint256 minPeriod = policy.startPeriod; + function calculateRefund(Policy storage _policy, address _node) internal returns (uint256) { + 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.getDowntimePeriodsLength(policy.node); - for (uint256 i = policy.indexOfDowntimePeriods; i < length; i++) { + uint256 length = escrow.getDowntimePeriodsLength(_node); + for (uint256 i = arrangement.indexOfDowntimePeriods; i < length; i++) { uint256 startPeriod; uint256 endPeriod; - (startPeriod, endPeriod) = escrow.getDowntimePeriods(policy.node, i); + (startPeriod, endPeriod) = escrow.getDowntimePeriods(_node, i); if (startPeriod > maxPeriod) { break; } else if (endPeriod < minPeriod) { continue; } - uint256 max = Math.min256(maxPeriod, endPeriod); - uint256 min = Math.max256(minPeriod, startPeriod); - downtimePeriods = downtimePeriods.add(max.sub(min).add(1)); + downtimePeriods = downtimePeriods.add( + Math.min256(maxPeriod, endPeriod) + .sub(Math.max256(minPeriod, startPeriod)) + .add(uint(1))); if (maxPeriod <= endPeriod) { break; } } - policy.indexOfDowntimePeriods = i; - uint256 lastActivePeriod = escrow.getLastActivePeriod(policy.node); + arrangement.indexOfDowntimePeriods = i; + uint256 lastActivePeriod = escrow.getLastActivePeriod(_node); if (i == length && lastActivePeriod < maxPeriod) { - min = Math.max256(minPeriod.sub(1), lastActivePeriod); - downtimePeriods = downtimePeriods.add(maxPeriod.sub(min)); + downtimePeriods = downtimePeriods.add( + maxPeriod.sub(Math.max256( + minPeriod.sub(uint(1)), lastActivePeriod))); } - policy.startPeriod = maxPeriod.add(1); + arrangement.lastRefundedPeriod = maxPeriod.add(uint(1)); - return policy.rate.mul(downtimePeriods); + return _policy.rate.mul(downtimePeriods); + } + + /** + * @notice Get number of nodes in policy + * @param _policyId Policy id + **/ + function getPolicyNodesLength(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]; } /** * @dev Used only in verifyState(address) method **/ - function getPolicyField(bytes20 _policyId, uint8 _field) + function getPolicyField(bytes20 _policyId, uint8 _field, address _node) public view returns (bytes32) { Policy storage policy = policies[_policyId]; if (_field == 0) { return bytes32(policy.client); } else if (_field == 1) { - return bytes32(policy.node); - } else if (_field == 2) { return bytes32(policy.rate); - } else if (_field == 3) { + } else if (_field == 2) { return bytes32(policy.startPeriod); - } else if (_field == 4) { + } else if (_field == 3) { return bytes32(policy.lastPeriod); + } else if (_field == 4) { + return policy.disabled ? bytes32(1) : bytes32(0); } else if (_field == 5) { - return bytes32(policy.indexOfDowntimePeriods); + return bytes32(policy.arrangements[_node].indexOfDowntimePeriods); + } else if (_field == 6) { + return bytes32(policy.arrangements[_node].lastRefundedPeriod); + } else if (_field == 7) { + return policy.arrangements[_node].disabled ? bytes32(1) : bytes32(0); } } @@ -252,38 +385,55 @@ contract PolicyManager is Upgradeable { * @dev Used only in verifyState(address) method **/ function getNodeInfoField(address _node, uint8 _field, uint256 _period) - public view returns (uint256) + public view returns (bytes32) { NodeInfo storage nodeInfo = nodes[_node]; if (_field == 0) { - return nodeInfo.reward; + return bytes32(nodeInfo.reward); } else if (_field == 1) { - return nodeInfo.rewardByPeriod[_period]; + return bytes32(nodeInfo.lastRewardByPeriod); + } else if (_field == 2) { + return bytes32(nodeInfo.lastMinedPeriod); + } else if (_field == 3) { + return bytes32(nodeInfo.rewardChanges[_period]); } } function verifyState(address _testTarget) public onlyOwner { require(address(delegateGet(_testTarget, "escrow()")) == address(escrow)); Policy storage policy = policies[RESERVED_POLICY_ID]; - require(address(delegateGet(_testTarget, "getPolicyField(bytes20,uint8)", - RESERVED_POLICY_ID, 0)) == policy.client); - require(address(delegateGet(_testTarget, "getPolicyField(bytes20,uint8)", - RESERVED_POLICY_ID, 1)) == policy.node); - require(uint256(delegateGet(_testTarget, "getPolicyField(bytes20,uint8)", - RESERVED_POLICY_ID, 2)) == policy.rate); - require(uint256(delegateGet(_testTarget, "getPolicyField(bytes20,uint8)", - RESERVED_POLICY_ID, 3)) == policy.startPeriod); - require(uint256(delegateGet(_testTarget, "getPolicyField(bytes20,uint8)", - RESERVED_POLICY_ID, 4)) == policy.lastPeriod); - require(uint256(delegateGet(_testTarget, "getPolicyField(bytes20,uint8)", - RESERVED_POLICY_ID, 5)) == policy.indexOfDowntimePeriods); + require(address(delegateGet(_testTarget, "getPolicyField(bytes20,uint8,address)", + RESERVED_POLICY_ID, 0, 0x0)) == policy.client); + require(uint256(delegateGet(_testTarget, "getPolicyField(bytes20,uint8,address)", + RESERVED_POLICY_ID, 1, 0x0)) == policy.rate); + require(uint256(delegateGet(_testTarget, "getPolicyField(bytes20,uint8,address)", + RESERVED_POLICY_ID, 2, 0x0)) == policy.startPeriod); + require(uint256(delegateGet(_testTarget, "getPolicyField(bytes20,uint8,address)", + RESERVED_POLICY_ID, 3, 0x0)) == policy.lastPeriod); + require((delegateGet(_testTarget, "getPolicyField(bytes20,uint8,address)", + RESERVED_POLICY_ID, 4, 0x0) == bytes32(1)) == policy.disabled); + require(uint256(delegateGet(_testTarget, "getPolicyField(bytes20,uint8,address)", + RESERVED_POLICY_ID, 5, bytes32(RESERVED_NODE))) == + policy.arrangements[RESERVED_NODE].indexOfDowntimePeriods); + require(uint256(delegateGet(_testTarget, "getPolicyField(bytes20,uint8,address)", + RESERVED_POLICY_ID, 6, bytes32(RESERVED_NODE))) == + policy.arrangements[RESERVED_NODE].lastRefundedPeriod); + require((delegateGet(_testTarget, "getPolicyField(bytes20,uint8,address)", + RESERVED_POLICY_ID, 7, bytes32(RESERVED_NODE)) == bytes32(1)) == + policy.arrangements[RESERVED_NODE].disabled); + 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]); NodeInfo storage nodeInfo = nodes[RESERVED_NODE]; require(uint256(delegateGet(_testTarget, "getNodeInfoField(address,uint8,uint256)", bytes32(RESERVED_NODE), 0, 0)) == nodeInfo.reward); require(uint256(delegateGet(_testTarget, "getNodeInfoField(address,uint8,uint256)", - bytes32(RESERVED_NODE), 1, 22)) == nodeInfo.rewardByPeriod[22]); + bytes32(RESERVED_NODE), 1, 0)) == nodeInfo.lastRewardByPeriod); require(uint256(delegateGet(_testTarget, "getNodeInfoField(address,uint8,uint256)", - bytes32(RESERVED_NODE), 1, 44)) == nodeInfo.rewardByPeriod[44]); + bytes32(RESERVED_NODE), 2, 0)) == nodeInfo.lastMinedPeriod); + require(int256(delegateGet(_testTarget, "getNodeInfoField(address,uint8,uint256)", + bytes32(RESERVED_NODE), 3, 11)) == nodeInfo.rewardChanges[11]); } function finishUpgrade(address _target) public onlyOwner { @@ -292,15 +442,18 @@ contract PolicyManager is Upgradeable { // Create fake Policy and NodeInfo to use them in verifyState(address) Policy storage policy = policies[RESERVED_POLICY_ID]; policy.client = owner; - policy.node = owner; policy.startPeriod = 1; policy.lastPeriod = 2; policy.rate = 3; - policy.indexOfDowntimePeriods = 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].disabled = true; NodeInfo storage nodeInfo = nodes[RESERVED_NODE]; nodeInfo.reward = 100; - nodeInfo.rewardByPeriod[22] = 33; - nodeInfo.rewardByPeriod[44] = 55; + nodeInfo.lastRewardByPeriod = 33; + nodeInfo.lastMinedPeriod = 44; + nodeInfo.rewardChanges[11] = 55; } - -} +} \ No newline at end of file diff --git a/nkms_eth/project/contracts/lib/AdditionalMath.sol b/nkms_eth/project/contracts/lib/AdditionalMath.sol index 453d4aaee..7a8d0ebbc 100644 --- a/nkms_eth/project/contracts/lib/AdditionalMath.sol +++ b/nkms_eth/project/contracts/lib/AdditionalMath.sol @@ -17,4 +17,44 @@ library AdditionalMath { return (a.add(b) - 1) / b; } + /** + * @dev Adds unsigned value to signed value, throws on overflow. + */ + function add(int256 a, uint256 b) internal pure returns (int256) { + int256 c = a + int256(b); + assert(c >= a); + return c; + } + + /** + * @dev Subtracts two numbers, throws on overflow. + */ + function sub(int256 a, uint256 b) internal pure returns (int256) { + int256 c = a - int256(b); + assert(c <= a); + return c; + } + + /** + * @dev Adds signed value to unsigned value, throws on overflow. + */ + function add(uint256 a, int256 b) internal pure returns (uint256) { + if (b >= 0) { + return a.add(uint256(b)); + } else { + return a.sub(uint256(-b)); + } + } + + /** + * @dev Subtracts signed value from unsigned value, throws on overflow. + */ + function sub(uint256 a, int256 b) internal pure returns (uint256) { + if (b >= 0) { + return a.sub(uint256(b)); + } else { + return a.add(uint256(-b)); + } + } + } diff --git a/scripts/estimate_gas_tester.py b/scripts/estimate_gas_tester.py index 6ce7432cc..fb88eaf3d 100644 --- a/scripts/estimate_gas_tester.py +++ b/scripts/estimate_gas_tester.py @@ -7,12 +7,13 @@ A simple Python script to deploy contracts and then estimate gas for different m from nkms_eth.blockchain import TesterBlockchain from nkms_eth.escrow import Escrow from nkms_eth.token import NuCypherKMSToken +import os def main(): testerchain = TesterBlockchain() chain, web3 = testerchain._chain, testerchain._chain.web3 - creator, ursula1, ursula2, ursula3, *everyone_else = web3.eth.accounts + creator, ursula1, ursula2, ursula3, alice1, *everyone_else = web3.eth.accounts print("Web3 providers are", web3.providers) @@ -37,7 +38,7 @@ def main(): # Pre deposit tokens tx = token.transact({'from': creator}).approve(escrow.contract.address, 10 ** 7) chain.wait.for_receipt(tx) - print("Pre-deposit tokens fro 5 owners = " + + print("Pre-deposit tokens for 5 owners = " + str(escrow.contract.estimateGas({'from': creator}).preDeposit( web3.eth.accounts[4:9], [10 ** 6] * 5, [1] * 5))) @@ -179,6 +180,109 @@ def main(): tx = escrow.transact({'from': ursula3}).mint() chain.wait.for_receipt(tx) + # Create policy + policy_id_1 = os.urandom(20) + policy_id_2 = os.urandom(20) + number_of_periods = 10 + print("First creating policy (1 node, 10 periods) = " + + str(policy_manager.estimateGas({'from': alice1, 'value': 10000}) + .createPolicy(policy_id_1, number_of_periods, [ursula1]))) + tx = policy_manager.transact({'from': alice1, 'value': 10000})\ + .createPolicy(policy_id_1, number_of_periods, [ursula1]) + chain.wait.for_receipt(tx) + print("Second creating policy (1 node, 10 periods) = " + + str(policy_manager.estimateGas({'from': alice1, 'value': 10000}) + .createPolicy(policy_id_2, number_of_periods, [ursula1]))) + tx = policy_manager.transact({'from': alice1, 'value': 10000}) \ + .createPolicy(policy_id_2, number_of_periods, [ursula1]) + chain.wait.for_receipt(tx) + + # Revoke policy + print("Revoking policy = " + + str(policy_manager.estimateGas({'from': alice1}).revokePolicy(policy_id_1))) + tx = policy_manager.transact({'from': alice1}).revokePolicy(policy_id_1) + chain.wait.for_receipt(tx) + tx = policy_manager.transact({'from': alice1}).revokePolicy(policy_id_2) + chain.wait.for_receipt(tx) + + # Create policy with more periods + policy_id_1 = os.urandom(20) + policy_id_2 = os.urandom(20) + policy_id_3 = os.urandom(20) + number_of_periods = 100 + print("First creating policy (1 node, " + str(number_of_periods) + " periods) = " + + str(policy_manager.estimateGas({'from': alice1, 'value': 10000}) + .createPolicy(policy_id_1, number_of_periods, [ursula2]))) + tx = policy_manager.transact({'from': alice1, 'value': 10000})\ + .createPolicy(policy_id_1, number_of_periods, [ursula2]) + chain.wait.for_receipt(tx) + testerchain.wait_time(1) + print("Second creating policy (1 node, " + str(number_of_periods) + " periods) = " + + str(policy_manager.estimateGas({'from': alice1, 'value': 10000}) + .createPolicy(policy_id_2, number_of_periods, [ursula2]))) + tx = policy_manager.transact({'from': alice1, 'value': 10000}) \ + .createPolicy(policy_id_2, number_of_periods, [ursula2]) + chain.wait.for_receipt(tx) + print("Third creating policy (1 node, " + str(number_of_periods) + " periods) = " + + str(policy_manager.estimateGas({'from': alice1, 'value': 10000}) + .createPolicy(policy_id_3, number_of_periods, [ursula1]))) + tx = policy_manager.transact({'from': alice1, 'value': 10000}) \ + .createPolicy(policy_id_3, number_of_periods, [ursula1]) + chain.wait.for_receipt(tx) + + # Mine and revoke policy + testerchain.wait_time(10) + tx = escrow.transact({'from': ursula2}).confirmActivity() + chain.wait.for_receipt(tx) + tx = escrow.transact({'from': ursula1}).confirmActivity() + chain.wait.for_receipt(tx) + + testerchain.wait_time(1) + print("First mining after downtime = " + str(escrow.contract.estimateGas({'from': ursula1}).mint())) + tx = escrow.transact({'from': ursula1}).mint() + chain.wait.for_receipt(tx) + print("Second mining after downtime = " + str(escrow.contract.estimateGas({'from': ursula2}).mint())) + tx = escrow.transact({'from': ursula2}).mint() + chain.wait.for_receipt(tx) + + testerchain.wait_time(10) + print("First revoking policy after downtime = " + + str(policy_manager.estimateGas({'from': alice1}).revokePolicy(policy_id_1))) + tx = policy_manager.transact({'from': alice1}).revokePolicy(policy_id_1) + chain.wait.for_receipt(tx) + print("Second revoking policy after downtime = " + + str(policy_manager.estimateGas({'from': alice1}).revokePolicy(policy_id_2))) + tx = policy_manager.transact({'from': alice1}).revokePolicy(policy_id_2) + chain.wait.for_receipt(tx) + print("Second revoking policy after downtime = " + + str(policy_manager.estimateGas({'from': alice1}).revokePolicy(policy_id_3))) + tx = policy_manager.transact({'from': alice1}).revokePolicy(policy_id_3) + chain.wait.for_receipt(tx) + + # Create policy with multiple nodes + policy_id_1 = os.urandom(20) + policy_id_2 = os.urandom(20) + policy_id_3 = os.urandom(20) + number_of_periods = 100 + print("First creating policy (3 nodes, 100 periods) = " + + str(policy_manager.estimateGas({'from': alice1, 'value': 30000}) + .createPolicy(policy_id_1, number_of_periods, [ursula1, ursula2, ursula3]))) + tx = policy_manager.transact({'from': alice1, 'value': 30000}) \ + .createPolicy(policy_id_1, number_of_periods, [ursula1, ursula2, ursula3]) + chain.wait.for_receipt(tx) + print("Second creating policy (3 nodes, 100 periods) = " + + str(policy_manager.estimateGas({'from': alice1, 'value': 10000}) + .createPolicy(policy_id_2, number_of_periods, [ursula1, ursula2, ursula3]))) + tx = policy_manager.transact({'from': alice1, 'value': 10000}) \ + .createPolicy(policy_id_2, number_of_periods, [ursula1, ursula2, ursula3]) + chain.wait.for_receipt(tx) + print("Third creating policy (2 nodes, 100 periods) = " + + str(policy_manager.estimateGas({'from': alice1, 'value': 20000}) + .createPolicy(policy_id_3, number_of_periods, [ursula1, ursula2]))) + tx = policy_manager.transact({'from': alice1, 'value': 20000}) \ + .createPolicy(policy_id_3, number_of_periods, [ursula1, ursula2]) + chain.wait.for_receipt(tx) + print("All done!") diff --git a/tests/contracts/contracts/MinersEscrowForPolicyMock.sol b/tests/contracts/contracts/MinersEscrowForPolicyMock.sol index 80e2f2273..cd5cf7256 100644 --- a/tests/contracts/contracts/MinersEscrowForPolicyMock.sol +++ b/tests/contracts/contracts/MinersEscrowForPolicyMock.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.8; +pragma solidity ^0.4.18; import "contracts/PolicyManager.sol"; @@ -16,16 +16,18 @@ contract MinersEscrowForPolicyMock { PolicyManager public policyManager; uint256 public secondsPerPeriod; - address public node; + mapping(address => bool) public nodes; uint256 public lastActivePeriod; Downtime[] public downtime; /** - * @param _node Address of node that allow to use policy manager + * @param _nodes Addresses of nodes that allow to use policy manager * @param _minutesPerPeriod Size of period in minutes **/ - function MinersEscrowForPolicyMock(address _node, uint256 _minutesPerPeriod) public { - node = _node; + function MinersEscrowForPolicyMock(address[] _nodes, uint256 _minutesPerPeriod) public { + for (uint256 i = 0; i < _nodes.length; i++) { + nodes[_nodes[i]] = true; + } secondsPerPeriod = _minutesPerPeriod * 1 minutes; } @@ -35,7 +37,7 @@ contract MinersEscrowForPolicyMock { function getLockedTokens(address _owner) public view returns (uint256) { - if (_owner == node) { + if (nodes[_owner]) { return 1; } return 0; @@ -67,7 +69,7 @@ contract MinersEscrowForPolicyMock { * @param _period Period for minting **/ function mint(uint256 _period) external { - policyManager.updateReward(node, _period); + policyManager.updateReward(msg.sender, _period); } /** diff --git a/tests/contracts/test_policy_manager.py b/tests/contracts/test_policy_manager.py index 999a52fa3..5a6066f9d 100644 --- a/tests/contracts/test_policy_manager.py +++ b/tests/contracts/test_policy_manager.py @@ -6,10 +6,13 @@ import os @pytest.fixture() def escrow(web3, chain): creator = web3.eth.accounts[0] - node = web3.eth.accounts[1] + node1 = web3.eth.accounts[3] + node2 = web3.eth.accounts[4] + node3 = web3.eth.accounts[5] # Creator deploys the escrow escrow, _ = chain.provider.get_or_deploy_contract( - 'MinersEscrowForPolicyMock', deploy_args=[node, MINUTES_IN_PERIOD], + 'MinersEscrowForPolicyMock', + deploy_args=[[node1, node2, node3], MINUTES_IN_PERIOD], deploy_transaction={'from': creator}) return escrow @@ -17,7 +20,7 @@ def escrow(web3, chain): @pytest.fixture() def policy_manager(web3, chain, escrow): creator = web3.eth.accounts[0] - client = web3.eth.accounts[2] + client = web3.eth.accounts[1] # Creator deploys the policy manager policy_manager, _ = chain.provider.get_or_deploy_contract( @@ -44,6 +47,7 @@ def wait_time(chain, wait_periods): MINUTES_IN_PERIOD = 10 policy_id = os.urandom(20) policy_id_2 = os.urandom(20) +policy_id_3 = os.urandom(20) rate = 20 number_of_periods = 10 value = rate * number_of_periods @@ -51,35 +55,43 @@ value = rate * number_of_periods def test_create_revoke(web3, chain, escrow, policy_manager): creator = web3.eth.accounts[0] - node = web3.eth.accounts[1] - client = web3.eth.accounts[2] - bad_node = web3.eth.accounts[3] + client = web3.eth.accounts[1] + bad_node = web3.eth.accounts[2] + node1 = web3.eth.accounts[3] + node2 = web3.eth.accounts[4] + node3 = web3.eth.accounts[5] client_balance = web3.eth.getBalance(client) # Try create policy for bad node with pytest.raises(TransactionFailed): tx = policy_manager.transact({'from': client, 'value': value})\ - .createPolicy(policy_id, bad_node, 1) + .createPolicy(policy_id, 1, [bad_node]) + chain.wait.for_receipt(tx) + with pytest.raises(TransactionFailed): + tx = policy_manager.transact({'from': client, 'value': value})\ + .createPolicy(policy_id, 1, [node1, bad_node]) chain.wait.for_receipt(tx) # Try create policy with no ETH with pytest.raises(TransactionFailed): tx = policy_manager.transact({'from': client})\ - .createPolicy(policy_id, node, 1) + .createPolicy(policy_id, 1, [node1]) chain.wait.for_receipt(tx) # Create policy period = escrow.call().getCurrentPeriod() tx = policy_manager.transact({'from': client, 'value': value, 'gas_price': 0})\ - .createPolicy(policy_id, node, number_of_periods) + .createPolicy(policy_id, number_of_periods, [node1]) chain.wait.for_receipt(tx) policy = policy_manager.call().policies(policy_id) assert 200 == web3.eth.getBalance(policy_manager.address) assert client_balance - 200 == web3.eth.getBalance(client) assert client == policy[0] - assert node == policy[1] - assert rate == policy[2] - assert period + 1 == policy[3] - assert period + 10 == policy[4] + assert rate == policy[1] + assert period + 1 == policy[2] + assert period + 10 == policy[3] + assert not policy[4] + assert 1 == policy_manager.call().getPolicyNodesLength(policy_id) + assert node1 == policy_manager.call().getPolicyNode(policy_id, 0) events = policy_manager.pastEvents('PolicyCreated').get() assert 1 == len(events) @@ -87,12 +99,12 @@ def test_create_revoke(web3, chain, escrow, policy_manager): # TODO change when v4 of web3.py is released assert policy_id == event_args['policyId'].encode('latin-1') assert client.lower() == event_args['client'].lower() - assert node.lower() == event_args['node'].lower() + # assert node.lower() == event_args['nodes'][0].lower() # Try to create policy again with pytest.raises(TransactionFailed): tx = policy_manager.transact({'from': client, 'value': value}) \ - .createPolicy(policy_id, node, number_of_periods) + .createPolicy(policy_id, number_of_periods, [node1]) chain.wait.for_receipt(tx) # Only client can revoke policy @@ -102,27 +114,46 @@ def test_create_revoke(web3, chain, escrow, policy_manager): tx = policy_manager.transact({'from': client, 'gas_price': 0}).revokePolicy(policy_id) chain.wait.for_receipt(tx) policy = policy_manager.call().policies(policy_id) - assert '0x' + '0' * 40 == policy[0] + assert policy[4] events = policy_manager.pastEvents('PolicyRevoked').get() assert 1 == len(events) event_args = events[0]['args'] # TODO change when v4 of web3.py is released assert policy_id == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert value == event_args['value'] + events = policy_manager.pastEvents('ArrangementRevoked').get() + assert 1 == len(events) + event_args = events[0]['args'] + # TODO change when v4 of web3.py is released + assert policy_id == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node1.lower() == event_args['node'].lower() + assert value == event_args['value'] + + # Can't revoke again + with pytest.raises(TransactionFailed): + tx = policy_manager.transact({'from': client}).revokePolicy(policy_id) + chain.wait.for_receipt(tx) + with pytest.raises(TransactionFailed): + tx = policy_manager.transact({'from': client}).revokeArrangement(policy_id, node1) + chain.wait.for_receipt(tx) # Create another policy period = escrow.call().getCurrentPeriod() - tx = policy_manager.transact({'from': client, 'value': value, 'gas_price': 0})\ - .createPolicy(policy_id_2, node, number_of_periods) + tx = policy_manager.transact({'from': client, 'value': 3 * value, 'gas_price': 0})\ + .createPolicy(policy_id_2, number_of_periods, [node1, node2, node3]) chain.wait.for_receipt(tx) + assert 3 * value == web3.eth.getBalance(policy_manager.address) + assert client_balance - 3 * value == web3.eth.getBalance(client) policy = policy_manager.call().policies(policy_id_2) - assert 200 == web3.eth.getBalance(policy_manager.address) - assert client_balance - 200 == web3.eth.getBalance(client) assert client == policy[0] - assert node == policy[1] - assert rate == policy[2] - assert period + 1 == policy[3] - assert period + 10 == policy[4] + assert rate == policy[1] + assert period + 1 == policy[2] + assert period + 10 == policy[3] + assert not policy[4] + # assert node.lower() == event_args['node'].lower() events = policy_manager.pastEvents('PolicyCreated').get() assert 2 == len(events) @@ -130,80 +161,148 @@ def test_create_revoke(web3, chain, escrow, policy_manager): # TODO change when v4 of web3.py is released assert policy_id_2 == event_args['policyId'].encode('latin-1') assert client.lower() == event_args['client'].lower() - assert node.lower() == event_args['node'].lower() + # assert node.lower() == event_args['node'].lower() - events = policy_manager.pastEvents('Refunded').get() - assert 1 == len(events) + tx = policy_manager.transact({'from': client, 'gas_price': 0})\ + .revokeArrangement(policy_id_2, node1) + chain.wait.for_receipt(tx) + assert 2 * value == web3.eth.getBalance(policy_manager.address) + assert client_balance - 2 * value == web3.eth.getBalance(client) + assert not policy[4] + + events = policy_manager.pastEvents('ArrangementRevoked').get() + assert 2 == len(events) + event_args = events[1]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node1.lower() == event_args['node'].lower() + assert value == event_args['value'] + + # Can't revoke again + with pytest.raises(TransactionFailed): + tx = policy_manager.transact({'from': client}).revokeArrangement(policy_id_2, node1) + chain.wait.for_receipt(tx) + + tx = policy_manager.transact({'from': client, 'gas_price': 0}).revokePolicy(policy_id_2) + chain.wait.for_receipt(tx) + assert 0 == web3.eth.getBalance(policy_manager.address) + assert client_balance == web3.eth.getBalance(client) + assert not policy[4] + + events = policy_manager.pastEvents('ArrangementRevoked').get() + assert 4 == len(events) + event_args = events[2]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node2.lower() == event_args['node'].lower() + assert value == event_args['value'] + event_args = events[3]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node3.lower() == event_args['node'].lower() + assert value == event_args['value'] + events = policy_manager.pastEvents('PolicyRevoked').get() + assert 2 == len(events) + event_args = events[1]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert 2 * value == event_args['value'] + + # Can't revoke again + with pytest.raises(TransactionFailed): + tx = policy_manager.transact({'from': client}).revokePolicy(policy_id_2) + chain.wait.for_receipt(tx) + with pytest.raises(TransactionFailed): + tx = policy_manager.transact({'from': client}).revokeArrangement(policy_id_2, node1) + chain.wait.for_receipt(tx) + + events = policy_manager.pastEvents('RefundForArrangement').get() + assert 0 == len(events) + events = policy_manager.pastEvents('RefundForPolicy').get() + assert 0 == len(events) def test_reward(web3, chain, escrow, policy_manager): - node = web3.eth.accounts[1] - client = web3.eth.accounts[2] - node_balance = web3.eth.getBalance(node) + client = web3.eth.accounts[1] + node1 = web3.eth.accounts[3] + node2 = web3.eth.accounts[4] + node3 = web3.eth.accounts[5] + node_balance = web3.eth.getBalance(node1) + + # Mint period without policies + period = escrow.call().getCurrentPeriod() + tx = escrow.transact({'from': node1, 'gas_price': 0}).mint(period) + chain.wait.for_receipt(tx) + assert 0 == policy_manager.call().nodes(node1)[0] # Create policy - period = escrow.call().getCurrentPeriod() - tx = policy_manager.transact({'from': client, 'value': value})\ - .createPolicy(policy_id, node, number_of_periods) + tx = policy_manager.transact({'from': client, 'value': 3 * value})\ + .createPolicy(policy_id, number_of_periods, [node1, node2, node3]) chain.wait.for_receipt(tx) # Nothing to withdraw with pytest.raises(TransactionFailed): - tx = policy_manager.transact({'from': node}).withdraw() + tx = policy_manager.transact({'from': node1}).withdraw() chain.wait.for_receipt(tx) # Can't update reward directly with pytest.raises(TransactionFailed): - tx = policy_manager.transact({'from': node}).updateReward(node, period + 1) + tx = policy_manager.transact({'from': node1}).updateReward(node1, period + 1) chain.wait.for_receipt(tx) # Mint some periods for x in range(5): - tx = escrow.transact({'from': node, 'gas_price': 0}).mint(period) + tx = escrow.transact({'from': node1, 'gas_price': 0}).mint(period) chain.wait.for_receipt(tx) period += 1 - assert 80 == policy_manager.call().nodes(node) + assert 80 == policy_manager.call().nodes(node1)[0] # Withdraw - tx = policy_manager.transact({'from': node, 'gas_price': 0}).withdraw() + tx = policy_manager.transact({'from': node1, 'gas_price': 0}).withdraw() chain.wait.for_receipt(tx) - assert node_balance + 80 == web3.eth.getBalance(node) - assert 120 == web3.eth.getBalance(policy_manager.address) + assert node_balance + 80 == web3.eth.getBalance(node1) + assert 120 + 2 * value == web3.eth.getBalance(policy_manager.address) events = policy_manager.pastEvents('Withdrawn').get() assert 1 == len(events) event_args = events[0]['args'] - assert node.lower() == event_args['node'].lower() + assert node1.lower() == event_args['node'].lower() assert 80 == event_args['value'] # Mint more periods for x in range(20): - tx = escrow.transact({'from': node, 'gas_price': 0}).mint(period) + tx = escrow.transact({'from': node1, 'gas_price': 0}).mint(period) chain.wait.for_receipt(tx) period += 1 - assert 120 == policy_manager.call().nodes(node) + assert 120 == policy_manager.call().nodes(node1)[0] # Withdraw - tx = policy_manager.transact({'from': node, 'gas_price': 0}).withdraw() + tx = policy_manager.transact({'from': node1, 'gas_price': 0}).withdraw() chain.wait.for_receipt(tx) - assert node_balance + 200 == web3.eth.getBalance(node) - assert 0 == web3.eth.getBalance(policy_manager.address) + assert node_balance + value == web3.eth.getBalance(node1) + assert 2 * value == web3.eth.getBalance(policy_manager.address) events = policy_manager.pastEvents('Withdrawn').get() assert 2 == len(events) event_args = events[1]['args'] - assert node.lower() == event_args['node'].lower() + assert node1.lower() == event_args['node'].lower() assert 120 == event_args['value'] def test_refund(web3, chain, escrow, policy_manager): - node = web3.eth.accounts[1] - client = web3.eth.accounts[2] + client = web3.eth.accounts[1] + node1 = web3.eth.accounts[3] + node2 = web3.eth.accounts[4] + node3 = web3.eth.accounts[5] client_balance = web3.eth.getBalance(client) # Create policy tx = policy_manager.transact({'from': client, 'value': value, 'gas_price': 0}) \ - .createPolicy(policy_id, node, number_of_periods) + .createPolicy(policy_id, number_of_periods, [node1]) chain.wait.for_receipt(tx) tx = escrow.transact().setLastActivePeriod(escrow.call().getCurrentPeriod()) chain.wait.for_receipt(tx) @@ -216,7 +315,15 @@ def test_refund(web3, chain, escrow, policy_manager): assert client_balance - 20 == web3.eth.getBalance(client) assert client == policy_manager.call().policies(policy_id)[0] - events = policy_manager.pastEvents('Refunded').get() + events = policy_manager.pastEvents('RefundForArrangement').get() + assert 1 == len(events) + event_args = events[0]['args'] + # TODO change when v4 of web3.py is released + assert policy_id == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node1.lower() == event_args['node'].lower() + assert 180 == event_args['value'] + events = policy_manager.pastEvents('RefundForPolicy').get() assert 1 == len(events) event_args = events[0]['args'] # TODO change when v4 of web3.py is released @@ -229,9 +336,17 @@ def test_refund(web3, chain, escrow, policy_manager): chain.wait.for_receipt(tx) assert 0 == web3.eth.getBalance(policy_manager.address) assert client_balance == web3.eth.getBalance(client) - assert '0x' + '0' * 40 == policy_manager.call().policies(policy_id)[0] + assert policy_manager.call().policies(policy_id)[4] - events = policy_manager.pastEvents('Refunded').get() + events = policy_manager.pastEvents('RefundForArrangement').get() + assert 2 == len(events) + event_args = events[1]['args'] + # TODO change when v4 of web3.py is released + assert policy_id == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node1.lower() == event_args['node'].lower() + assert 20 == event_args['value'] + events = policy_manager.pastEvents('RefundForPolicy').get() assert 2 == len(events) event_args = events[1]['args'] # TODO change when v4 of web3.py is released @@ -239,68 +354,145 @@ def test_refund(web3, chain, escrow, policy_manager): assert client.lower() == event_args['client'].lower() assert 20 == event_args['value'] + # Can't refund again + with pytest.raises(TransactionFailed): + tx = policy_manager.transact({'from': client}).refund(policy_id) + chain.wait.for_receipt(tx) + with pytest.raises(TransactionFailed): + tx = policy_manager.transact({'from': client}).refund(policy_id, node1) + chain.wait.for_receipt(tx) + # Create policy again period = escrow.call().getCurrentPeriod() - tx = policy_manager.transact({'from': client, 'value': value, 'gas_price': 0})\ - .createPolicy(policy_id, node, number_of_periods) + tx = policy_manager.transact({'from': client, 'value': 3 * value, 'gas_price': 0})\ + .createPolicy(policy_id_2, number_of_periods, [node1, node2, node3]) chain.wait.for_receipt(tx) # Nothing to refund - tx = policy_manager.transact({'from': client, 'gas_price': 0}).refund(policy_id) + tx = policy_manager.transact({'from': client, 'gas_price': 0}).refund(policy_id_2) chain.wait.for_receipt(tx) - assert 200 == web3.eth.getBalance(policy_manager.address) - assert client_balance - 200 == web3.eth.getBalance(client) - events = policy_manager.pastEvents('Refunded').get() - assert 2 == len(events) + tx = policy_manager.transact({'from': client, 'gas_price': 0}).refund(policy_id_2, node1) + chain.wait.for_receipt(tx) + assert 3 * value == web3.eth.getBalance(policy_manager.address) + assert client_balance - 3 * value == web3.eth.getBalance(client) + events = policy_manager.pastEvents('RefundForArrangement').get() + assert 6 == len(events) + event_args = events[2]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node1.lower() == event_args['node'].lower() + assert 0 == event_args['value'] + event_args = events[3]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node2.lower() == event_args['node'].lower() + assert 0 == event_args['value'] + event_args = events[4]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node3.lower() == event_args['node'].lower() + assert 0 == event_args['value'] + event_args = events[5]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node1.lower() == event_args['node'].lower() + assert 0 == event_args['value'] + events = policy_manager.pastEvents('RefundForPolicy').get() + assert 3 == len(events) + event_args = events[2]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert 0 == event_args['value'] # Try to refund nonexistent policy with pytest.raises(TransactionFailed): - tx = policy_manager.transact({'from': client}).refund(policy_id_2) + tx = policy_manager.transact({'from': client}).refund(policy_id_3) chain.wait.for_receipt(tx) - # Node try to refund by node with pytest.raises(TransactionFailed): - tx = policy_manager.transact({'from': node}).refund(policy_id) + tx = policy_manager.transact({'from': node1}).refund(policy_id_2) chain.wait.for_receipt(tx) # Mint some periods and mark others as downtime periods period += 1 - tx = escrow.transact().mint(period) + tx = escrow.transact({'from': node1}).mint(period) chain.wait.for_receipt(tx) - tx = escrow.transact().mint(period + 1) + tx = escrow.transact({'from': node1}).mint(period + 1) chain.wait.for_receipt(tx) tx = escrow.transact().pushDowntimePeriod(period + 2, period + 3) chain.wait.for_receipt(tx) - tx = escrow.transact().mint(period + 4) + tx = escrow.transact({'from': node1}).mint(period + 4) chain.wait.for_receipt(tx) tx = escrow.transact().pushDowntimePeriod(period + 5, period + 7) chain.wait.for_receipt(tx) - tx = escrow.transact().mint(period + 8) + tx = escrow.transact({'from': node1}).mint(period + 8) chain.wait.for_receipt(tx) tx = escrow.transact().setLastActivePeriod(period + 8) chain.wait.for_receipt(tx) - assert 80 == policy_manager.call().nodes(node) + assert 80 == policy_manager.call().nodes(node1)[0] # Wait and refund wait_time(chain, 10) - tx = policy_manager.transact({'from': client, 'gas_price': 0}).refund(policy_id) + tx = policy_manager.transact({'from': client, 'gas_price': 0}).refund(policy_id_2, node1) chain.wait.for_receipt(tx) - assert 80 == web3.eth.getBalance(policy_manager.address) - assert client_balance - 80 == web3.eth.getBalance(client) - assert '0x' + '0' * 40 == policy_manager.call().policies(policy_id)[0] + assert 2 * value + 80 == web3.eth.getBalance(policy_manager.address) + assert client_balance - (2 * value + 80) == web3.eth.getBalance(client) + assert not policy_manager.call().policies(policy_id_2)[4] - events = policy_manager.pastEvents('Refunded').get() - assert 3 == len(events) - event_args = events[2]['args'] + events = policy_manager.pastEvents('RefundForArrangement').get() + assert 7 == len(events) + event_args = events[6]['args'] # TODO change when v4 of web3.py is released - assert policy_id == event_args['policyId'].encode('latin-1') + assert policy_id_2 == event_args['policyId'].encode('latin-1') assert client.lower() == event_args['client'].lower() + assert node1.lower() == event_args['node'].lower() assert 120 == event_args['value'] + events = policy_manager.pastEvents('RefundForPolicy').get() + assert 3 == len(events) + + # Can't refund arrangement again + with pytest.raises(TransactionFailed): + tx = policy_manager.transact({'from': client}).refund(policy_id, node1) + chain.wait.for_receipt(tx) + + # But can refund others + tx = policy_manager.transact({'from': client, 'gas_price': 0}).refund(policy_id_2) + chain.wait.for_receipt(tx) + assert 3 * 80 == web3.eth.getBalance(policy_manager.address) + assert client_balance - 3 * 80 == web3.eth.getBalance(client) + assert policy_manager.call().policies(policy_id_2)[4] + + events = policy_manager.pastEvents('RefundForArrangement').get() + assert 9 == len(events) + event_args = events[7]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node2.lower() == event_args['node'].lower() + assert 120 == event_args['value'] + event_args = events[8]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node3.lower() == event_args['node'].lower() + assert 120 == event_args['value'] + events = policy_manager.pastEvents('RefundForPolicy').get() + assert 4 == len(events) + event_args = events[3]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_2 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert 2 * 120 == event_args['value'] # Create policy again period = escrow.call().getCurrentPeriod() tx = policy_manager.transact({'from': client, 'value': value, 'gas_price': 0})\ - .createPolicy(policy_id, node, number_of_periods) + .createPolicy(policy_id_3, number_of_periods, [node1]) chain.wait.for_receipt(tx) # Mint some periods @@ -309,37 +501,47 @@ def test_refund(web3, chain, escrow, policy_manager): chain.wait.for_receipt(tx) for x in range(3): period += 1 - tx = escrow.transact({'from': node}).mint(period) + tx = escrow.transact({'from': node1}).mint(period) chain.wait.for_receipt(tx) tx = escrow.transact().setLastActivePeriod(period) chain.wait.for_receipt(tx) - assert 140 == policy_manager.call().nodes(node) + assert 140 == policy_manager.call().nodes(node1)[0] # Client revokes policy wait_time(chain, 4) - tx = policy_manager.transact({'from': client, 'gas_price': 0}).revokePolicy(policy_id) + tx = policy_manager.transact({'from': client, 'gas_price': 0}).revokePolicy(policy_id_3) chain.wait.for_receipt(tx) - policy = policy_manager.call().policies(policy_id) - assert 140 == web3.eth.getBalance(policy_manager.address) - assert client_balance - 140 == web3.eth.getBalance(client) - assert '0x' + '0' * 40 == policy[0] + policy = policy_manager.call().policies(policy_id_3) + assert 60 + 3 * 80 == web3.eth.getBalance(policy_manager.address) + assert client_balance - (60 + 3 * 80) == web3.eth.getBalance(client) + assert policy[4] - events = policy_manager.pastEvents('Refunded').get() + events = policy_manager.pastEvents('RefundForArrangement').get() + assert 9 == len(events) + events = policy_manager.pastEvents('RefundForPolicy').get() assert 4 == len(events) - event_args = events[3]['args'] + events = policy_manager.pastEvents('ArrangementRevoked').get() + assert 1 == len(events) + event_args = events[0]['args'] # TODO change when v4 of web3.py is released - assert policy_id == event_args['policyId'].encode('latin-1') + assert policy_id_3 == event_args['policyId'].encode('latin-1') + assert client.lower() == event_args['client'].lower() + assert node1.lower() == event_args['node'].lower() + assert 140 == event_args['value'] + events = policy_manager.pastEvents('PolicyRevoked').get() + assert 1 == len(events) + event_args = events[0]['args'] + # TODO change when v4 of web3.py is released + assert policy_id_3 == event_args['policyId'].encode('latin-1') assert client.lower() == event_args['client'].lower() assert 140 == event_args['value'] # Minting is useless after revoke for x in range(20): period += 1 - tx = escrow.transact({'from': node}).mint(period) + tx = escrow.transact({'from': node1}).mint(period) chain.wait.for_receipt(tx) - assert 140 == policy_manager.call().nodes(node) + assert 140 == policy_manager.call().nodes(node1)[0] events = policy_manager.pastEvents('PolicyCreated').get() assert 3 == len(events) - events = policy_manager.pastEvents('PolicyRevoked').get() - assert 1 == len(events)