mirror of https://github.com/nucypher/nucypher.git
Merge pull request #2619 from vzotova/batch-policy
Creating multiple policies in one txpull/2672/head
commit
a71d1de553
|
@ -0,0 +1 @@
|
||||||
|
PolicyManager: creating multiple policies in one tx
|
|
@ -17,7 +17,7 @@ import "contracts/proxy/Upgradeable.sol";
|
||||||
/**
|
/**
|
||||||
* @title PolicyManager
|
* @title PolicyManager
|
||||||
* @notice Contract holds policy data and locks accrued policy fees
|
* @notice Contract holds policy data and locks accrued policy fees
|
||||||
* @dev |v6.2.2|
|
* @dev |v6.3.1|
|
||||||
*/
|
*/
|
||||||
contract PolicyManager is Upgradeable {
|
contract PolicyManager is Upgradeable {
|
||||||
using SafeERC20 for NuCypherToken;
|
using SafeERC20 for NuCypherToken;
|
||||||
|
@ -253,68 +253,161 @@ contract PolicyManager is Upgradeable {
|
||||||
)
|
)
|
||||||
external payable
|
external payable
|
||||||
{
|
{
|
||||||
Policy storage policy = policies[_policyId];
|
|
||||||
require(
|
require(
|
||||||
_policyId != RESERVED_POLICY_ID &&
|
|
||||||
policy.feeRate == 0 &&
|
|
||||||
!policy.disabled &&
|
|
||||||
_endTimestamp > block.timestamp &&
|
_endTimestamp > block.timestamp &&
|
||||||
msg.value > 0
|
msg.value > 0
|
||||||
);
|
);
|
||||||
|
|
||||||
require(address(this).balance <= MAX_BALANCE);
|
require(address(this).balance <= MAX_BALANCE);
|
||||||
uint16 currentPeriod = getCurrentPeriod();
|
uint16 currentPeriod = getCurrentPeriod();
|
||||||
uint16 endPeriod = uint16(_endTimestamp / secondsPerPeriod) + 1;
|
uint16 endPeriod = uint16(_endTimestamp / secondsPerPeriod) + 1;
|
||||||
uint256 numberOfPeriods = endPeriod - currentPeriod;
|
uint256 numberOfPeriods = endPeriod - currentPeriod;
|
||||||
|
|
||||||
policy.sponsor = msg.sender;
|
uint128 feeRate = uint128(msg.value.div(_nodes.length) / numberOfPeriods);
|
||||||
policy.startTimestamp = uint64(block.timestamp);
|
require(feeRate > 0 && feeRate * numberOfPeriods * _nodes.length == msg.value);
|
||||||
policy.endTimestamp = _endTimestamp;
|
|
||||||
policy.feeRate = uint128(msg.value.div(_nodes.length) / numberOfPeriods);
|
Policy storage policy = createPolicy(_policyId, _policyOwner, _endTimestamp, feeRate, _nodes.length);
|
||||||
require(policy.feeRate > 0 && policy.feeRate * numberOfPeriods * _nodes.length == msg.value);
|
|
||||||
if (_policyOwner != msg.sender && _policyOwner != address(0)) {
|
|
||||||
policy.owner = _policyOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint256 i = 0; i < _nodes.length; i++) {
|
for (uint256 i = 0; i < _nodes.length; i++) {
|
||||||
address node = _nodes[i];
|
address node = _nodes[i];
|
||||||
require(node != RESERVED_NODE);
|
addFeeToNode(currentPeriod, endPeriod, node, feeRate, int256(feeRate));
|
||||||
NodeInfo storage nodeInfo = nodes[node];
|
|
||||||
require(nodeInfo.previousFeePeriod != 0 &&
|
|
||||||
nodeInfo.previousFeePeriod < currentPeriod &&
|
|
||||||
policy.feeRate >= getMinFeeRate(nodeInfo));
|
|
||||||
// Check default value for feeDelta
|
|
||||||
if (nodeInfo.feeDelta[currentPeriod] == DEFAULT_FEE_DELTA) {
|
|
||||||
nodeInfo.feeDelta[currentPeriod] = int256(policy.feeRate);
|
|
||||||
} else {
|
|
||||||
// Overflow protection removed, because ETH total supply less than uint255/int256
|
|
||||||
nodeInfo.feeDelta[currentPeriod] += int256(policy.feeRate);
|
|
||||||
}
|
|
||||||
if (nodeInfo.feeDelta[endPeriod] == DEFAULT_FEE_DELTA) {
|
|
||||||
nodeInfo.feeDelta[endPeriod] = -int256(policy.feeRate);
|
|
||||||
} else {
|
|
||||||
nodeInfo.feeDelta[endPeriod] -= int256(policy.feeRate);
|
|
||||||
}
|
|
||||||
// Reset to default value if needed
|
|
||||||
if (nodeInfo.feeDelta[currentPeriod] == 0) {
|
|
||||||
nodeInfo.feeDelta[currentPeriod] = DEFAULT_FEE_DELTA;
|
|
||||||
}
|
|
||||||
if (nodeInfo.feeDelta[endPeriod] == 0) {
|
|
||||||
nodeInfo.feeDelta[endPeriod] = DEFAULT_FEE_DELTA;
|
|
||||||
}
|
|
||||||
policy.arrangements.push(ArrangementInfo(node, 0, 0));
|
policy.arrangements.push(ArrangementInfo(node, 0, 0));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Create multiple policies with the same owner, nodes and length
|
||||||
|
* @dev Generate policy ids before creation
|
||||||
|
* @param _policyIds Policy ids
|
||||||
|
* @param _policyOwner Policy owner. Zero address means sender is owner
|
||||||
|
* @param _endTimestamp End timestamp of all policies in seconds
|
||||||
|
* @param _nodes Nodes that will handle all policies
|
||||||
|
*/
|
||||||
|
function createPolicies(
|
||||||
|
bytes16[] calldata _policyIds,
|
||||||
|
address _policyOwner,
|
||||||
|
uint64 _endTimestamp,
|
||||||
|
address[] calldata _nodes
|
||||||
|
)
|
||||||
|
external payable
|
||||||
|
{
|
||||||
|
require(
|
||||||
|
_endTimestamp > block.timestamp &&
|
||||||
|
msg.value > 0 &&
|
||||||
|
_policyIds.length > 1
|
||||||
|
);
|
||||||
|
|
||||||
|
require(address(this).balance <= MAX_BALANCE);
|
||||||
|
uint16 currentPeriod = getCurrentPeriod();
|
||||||
|
uint16 endPeriod = uint16(_endTimestamp / secondsPerPeriod) + 1;
|
||||||
|
uint256 numberOfPeriods = endPeriod - currentPeriod;
|
||||||
|
|
||||||
|
uint128 feeRate = uint128(msg.value.div(_nodes.length) / numberOfPeriods / _policyIds.length);
|
||||||
|
require(feeRate > 0 && feeRate * numberOfPeriods * _nodes.length * _policyIds.length == msg.value);
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < _policyIds.length; i++) {
|
||||||
|
Policy storage policy = createPolicy(_policyIds[i], _policyOwner, _endTimestamp, feeRate, _nodes.length);
|
||||||
|
|
||||||
|
for (uint256 j = 0; j < _nodes.length; j++) {
|
||||||
|
policy.arrangements.push(ArrangementInfo(_nodes[j], 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int256 fee = int256(_policyIds.length * feeRate);
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < _nodes.length; i++) {
|
||||||
|
address node = _nodes[i];
|
||||||
|
addFeeToNode(currentPeriod, endPeriod, node, feeRate, fee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Create policy
|
||||||
|
* @param _policyId Policy id
|
||||||
|
* @param _policyOwner Policy owner. Zero address means sender is owner
|
||||||
|
* @param _endTimestamp End timestamp of the policy in seconds
|
||||||
|
* @param _feeRate Fee rate for policy
|
||||||
|
* @param _nodesLength Number of nodes that will handle policy
|
||||||
|
*/
|
||||||
|
function createPolicy(
|
||||||
|
bytes16 _policyId,
|
||||||
|
address _policyOwner,
|
||||||
|
uint64 _endTimestamp,
|
||||||
|
uint128 _feeRate,
|
||||||
|
uint256 _nodesLength
|
||||||
|
)
|
||||||
|
internal returns (Policy storage policy)
|
||||||
|
{
|
||||||
|
policy = policies[_policyId];
|
||||||
|
require(
|
||||||
|
_policyId != RESERVED_POLICY_ID &&
|
||||||
|
policy.feeRate == 0 &&
|
||||||
|
!policy.disabled
|
||||||
|
);
|
||||||
|
|
||||||
|
policy.sponsor = msg.sender;
|
||||||
|
policy.startTimestamp = uint64(block.timestamp);
|
||||||
|
policy.endTimestamp = _endTimestamp;
|
||||||
|
policy.feeRate = _feeRate;
|
||||||
|
|
||||||
|
if (_policyOwner != msg.sender && _policyOwner != address(0)) {
|
||||||
|
policy.owner = _policyOwner;
|
||||||
|
}
|
||||||
|
|
||||||
emit PolicyCreated(
|
emit PolicyCreated(
|
||||||
_policyId,
|
_policyId,
|
||||||
msg.sender,
|
msg.sender,
|
||||||
_policyOwner == address(0) ? msg.sender : _policyOwner,
|
_policyOwner == address(0) ? msg.sender : _policyOwner,
|
||||||
policy.feeRate,
|
_feeRate,
|
||||||
policy.startTimestamp,
|
policy.startTimestamp,
|
||||||
policy.endTimestamp,
|
policy.endTimestamp,
|
||||||
_nodes.length
|
_nodesLength
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Increase fee rate for specified node
|
||||||
|
* @param _currentPeriod Current period
|
||||||
|
* @param _endPeriod End period of policy
|
||||||
|
* @param _node Node that will handle policy
|
||||||
|
* @param _feeRate Fee rate for one policy
|
||||||
|
* @param _overallFeeRate Fee rate for all policies
|
||||||
|
*/
|
||||||
|
function addFeeToNode(
|
||||||
|
uint16 _currentPeriod,
|
||||||
|
uint16 _endPeriod,
|
||||||
|
address _node,
|
||||||
|
uint128 _feeRate,
|
||||||
|
int256 _overallFeeRate
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
require(_node != RESERVED_NODE);
|
||||||
|
NodeInfo storage nodeInfo = nodes[_node];
|
||||||
|
require(nodeInfo.previousFeePeriod != 0 &&
|
||||||
|
nodeInfo.previousFeePeriod < _currentPeriod &&
|
||||||
|
_feeRate >= getMinFeeRate(nodeInfo));
|
||||||
|
// Check default value for feeDelta
|
||||||
|
if (nodeInfo.feeDelta[_currentPeriod] == DEFAULT_FEE_DELTA) {
|
||||||
|
nodeInfo.feeDelta[_currentPeriod] = _overallFeeRate;
|
||||||
|
} else {
|
||||||
|
// Overflow protection removed, because ETH total supply less than uint255/int256
|
||||||
|
nodeInfo.feeDelta[_currentPeriod] += _overallFeeRate;
|
||||||
|
}
|
||||||
|
if (nodeInfo.feeDelta[_endPeriod] == DEFAULT_FEE_DELTA) {
|
||||||
|
nodeInfo.feeDelta[_endPeriod] = -_overallFeeRate;
|
||||||
|
} else {
|
||||||
|
nodeInfo.feeDelta[_endPeriod] -= _overallFeeRate;
|
||||||
|
}
|
||||||
|
// Reset to default value if needed
|
||||||
|
if (nodeInfo.feeDelta[_currentPeriod] == 0) {
|
||||||
|
nodeInfo.feeDelta[_currentPeriod] = DEFAULT_FEE_DELTA;
|
||||||
|
}
|
||||||
|
if (nodeInfo.feeDelta[_endPeriod] == 0) {
|
||||||
|
nodeInfo.feeDelta[_endPeriod] = DEFAULT_FEE_DELTA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Get policy owner
|
* @notice Get policy owner
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -104,7 +104,7 @@ def test_create_revoke(testerchain, escrow, policy_manager):
|
||||||
# Can't create policy using timestamp from the past
|
# Can't create policy using timestamp from the past
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = policy_manager.functions.createPolicy(policy_id, policy_sponsor, current_timestamp -1, [node1])\
|
tx = policy_manager.functions.createPolicy(policy_id, policy_sponsor, current_timestamp -1, [node1])\
|
||||||
.transact({'from': policy_sponsor})
|
.transact({'from': policy_sponsor, 'value': value})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
# Create policy
|
# Create policy
|
||||||
|
@ -517,6 +517,240 @@ def test_create_revoke(testerchain, escrow, policy_manager):
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_multiple_policies(testerchain, escrow, policy_manager):
|
||||||
|
creator, policy_sponsor, bad_node, node1, node2, node3, policy_owner, *everyone_else = testerchain.client.accounts
|
||||||
|
|
||||||
|
rate = 20
|
||||||
|
one_period = 60 * 60
|
||||||
|
number_of_periods = 10
|
||||||
|
value = rate * number_of_periods
|
||||||
|
default_fee_delta = policy_manager.functions.DEFAULT_FEE_DELTA().call()
|
||||||
|
|
||||||
|
policy_sponsor_balance = testerchain.client.get_balance(policy_sponsor)
|
||||||
|
policy_created_log = policy_manager.events.PolicyCreated.createFilter(fromBlock='latest')
|
||||||
|
|
||||||
|
# Check registered nodes
|
||||||
|
assert 0 < policy_manager.functions.nodes(node1).call()[PREVIOUS_FEE_PERIOD_FIELD]
|
||||||
|
assert 0 < policy_manager.functions.nodes(node2).call()[PREVIOUS_FEE_PERIOD_FIELD]
|
||||||
|
assert 0 < policy_manager.functions.nodes(node3).call()[PREVIOUS_FEE_PERIOD_FIELD]
|
||||||
|
assert 0 == policy_manager.functions.nodes(bad_node).call()[PREVIOUS_FEE_PERIOD_FIELD]
|
||||||
|
current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
|
||||||
|
end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
|
||||||
|
|
||||||
|
policy_id_1 = os.urandom(POLICY_ID_LENGTH)
|
||||||
|
policy_id_2 = os.urandom(POLICY_ID_LENGTH)
|
||||||
|
policies = [policy_id_1, policy_id_2]
|
||||||
|
|
||||||
|
# Try to create policy for bad (unregistered) node
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = policy_manager.functions.createPolicies(policies, policy_sponsor, end_timestamp, [bad_node])\
|
||||||
|
.transact({'from': policy_sponsor, 'value': 2 * value})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = policy_manager.functions.createPolicies(policies, policy_sponsor, end_timestamp, [node1, bad_node])\
|
||||||
|
.transact({'from': policy_sponsor, 'value': 2 * value})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
# Try to create policy with no ETH
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = policy_manager.functions.createPolicies(policies, policy_sponsor, end_timestamp, [node1])\
|
||||||
|
.transact({'from': policy_sponsor})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
# Can't create policy using timestamp from the past
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = policy_manager.functions.createPolicies(policies, policy_sponsor, current_timestamp - 1, [node1])\
|
||||||
|
.transact({'from': policy_sponsor, 'value': 2 * value})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
# Can't create two policies with the same id
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = policy_manager.functions.createPolicies([policy_id_1, policy_id_1], policy_sponsor, end_timestamp, [node1]) \
|
||||||
|
.transact({'from': policy_sponsor, 'value': 2 * value, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
# Can't use createPolicies() method for only one policy
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = policy_manager.functions.createPolicies([policy_id_1], policy_sponsor, end_timestamp, [node1]) \
|
||||||
|
.transact({'from': policy_sponsor, 'value': value, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
# Create policy
|
||||||
|
current_period = escrow.functions.getCurrentPeriod().call()
|
||||||
|
tx = policy_manager.functions.createPolicies(policies, policy_sponsor, end_timestamp, [node1])\
|
||||||
|
.transact({'from': policy_sponsor, 'value': 2 * value, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
|
||||||
|
# Check balances and policy info
|
||||||
|
assert 2 * value == testerchain.client.get_balance(policy_manager.address)
|
||||||
|
assert policy_sponsor_balance - 2 * value == testerchain.client.get_balance(policy_sponsor)
|
||||||
|
|
||||||
|
events = policy_created_log.get_all_entries()
|
||||||
|
assert len(events) == 2
|
||||||
|
|
||||||
|
for i, policy_id in enumerate(policies):
|
||||||
|
policy = policy_manager.functions.policies(policy_id).call()
|
||||||
|
assert policy_sponsor == policy[SPONSOR_FIELD]
|
||||||
|
assert NULL_ADDRESS == policy[OWNER_FIELD]
|
||||||
|
assert rate == policy[RATE_FIELD]
|
||||||
|
assert current_timestamp == policy[START_TIMESTAMP_FIELD]
|
||||||
|
assert end_timestamp == policy[END_TIMESTAMP_FIELD]
|
||||||
|
assert not policy[DISABLED_FIELD]
|
||||||
|
assert 1 == policy_manager.functions.getArrangementsLength(policy_id).call()
|
||||||
|
assert node1 == policy_manager.functions.getArrangementInfo(policy_id, 0).call()[0]
|
||||||
|
assert policy_sponsor == policy_manager.functions.getPolicyOwner(policy_id).call()
|
||||||
|
assert policy_manager.functions.getNodeFeeDelta(node1, current_period).call() == 2 * rate
|
||||||
|
assert policy_manager.functions.getNodeFeeDelta(node1, current_period + number_of_periods).call() == -2 * rate
|
||||||
|
|
||||||
|
event_args = events[i]['args']
|
||||||
|
assert policy_id == event_args['policyId']
|
||||||
|
assert policy_sponsor == event_args['sponsor']
|
||||||
|
assert policy_sponsor == event_args['owner']
|
||||||
|
assert rate == event_args['feeRate']
|
||||||
|
assert current_timestamp == event_args['startTimestamp']
|
||||||
|
assert end_timestamp == event_args['endTimestamp']
|
||||||
|
assert 1 == event_args['numberOfNodes']
|
||||||
|
|
||||||
|
# Can't create policy with the same id
|
||||||
|
policy_id_3 = os.urandom(POLICY_ID_LENGTH)
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = policy_manager.functions.createPolicies([policy_id_3, policy_id_1], policy_sponsor, end_timestamp, [node1])\
|
||||||
|
.transact({'from': policy_sponsor, 'value': 2 * value})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
# Revoke policies
|
||||||
|
tx = policy_manager.functions.revokePolicy(policy_id_1).transact({'from': policy_sponsor, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
tx = policy_manager.functions.revokePolicy(policy_id_2).transact({'from': policy_sponsor, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
assert policy_manager.functions.policies(policy_id_1).call()[DISABLED_FIELD]
|
||||||
|
assert policy_manager.functions.policies(policy_id_2).call()[DISABLED_FIELD]
|
||||||
|
|
||||||
|
# Create new policy
|
||||||
|
testerchain.time_travel(hours=1)
|
||||||
|
current_period = escrow.functions.getCurrentPeriod().call()
|
||||||
|
for period_to_set_default in range(current_period, current_period + number_of_periods + 1):
|
||||||
|
tx = escrow.functions.ping(node1, 0, 0, period_to_set_default).transact()
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
tx = escrow.functions.ping(node2, 0, 0, period_to_set_default).transact()
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
|
||||||
|
end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
|
||||||
|
policy_id_1 = os.urandom(POLICY_ID_LENGTH)
|
||||||
|
policy_id_2 = os.urandom(POLICY_ID_LENGTH)
|
||||||
|
policies = [policy_id_1, policy_id_2]
|
||||||
|
tx = policy_manager.functions.createPolicies(policies, policy_owner, end_timestamp, [node1, node2, node3])\
|
||||||
|
.transact({'from': policy_sponsor, 'value': 6 * value, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
|
||||||
|
assert 6 * value == testerchain.client.get_balance(policy_manager.address)
|
||||||
|
assert policy_sponsor_balance - 6 * value == testerchain.client.get_balance(policy_sponsor)
|
||||||
|
events = policy_created_log.get_all_entries()
|
||||||
|
assert len(events) == 4
|
||||||
|
|
||||||
|
for i, policy_id in enumerate(policies):
|
||||||
|
policy = policy_manager.functions.policies(policy_id).call()
|
||||||
|
assert policy_sponsor == policy[SPONSOR_FIELD]
|
||||||
|
assert policy_owner == policy[OWNER_FIELD]
|
||||||
|
assert rate == policy[RATE_FIELD]
|
||||||
|
assert current_timestamp == policy[START_TIMESTAMP_FIELD]
|
||||||
|
assert end_timestamp == policy[END_TIMESTAMP_FIELD]
|
||||||
|
assert not policy[DISABLED_FIELD]
|
||||||
|
assert policy_owner == policy_manager.functions.getPolicyOwner(policy_id).call()
|
||||||
|
assert policy_manager.functions.getNodeFeeDelta(node1, current_period).call() == default_fee_delta
|
||||||
|
assert policy_manager.functions.getNodeFeeDelta(node1, current_period + number_of_periods).call() == -2 * rate
|
||||||
|
assert policy_manager.functions.getNodeFeeDelta(node2, current_period).call() == 2 * rate
|
||||||
|
assert policy_manager.functions.getNodeFeeDelta(node2, current_period + number_of_periods).call() == -2 * rate
|
||||||
|
assert policy_manager.functions.getNodeFeeDelta(node3, current_period).call() == 2 * rate
|
||||||
|
assert policy_manager.functions.getNodeFeeDelta(node3, current_period + number_of_periods).call() == -2 * rate
|
||||||
|
|
||||||
|
event_args = events[i + 2]['args']
|
||||||
|
assert policy_id == event_args['policyId']
|
||||||
|
assert policy_sponsor == event_args['sponsor']
|
||||||
|
assert policy_owner == event_args['owner']
|
||||||
|
assert rate == event_args['feeRate']
|
||||||
|
assert current_timestamp == event_args['startTimestamp']
|
||||||
|
assert end_timestamp == event_args['endTimestamp']
|
||||||
|
assert 3 == event_args['numberOfNodes']
|
||||||
|
|
||||||
|
# Revoke policies
|
||||||
|
tx = policy_manager.functions.revokePolicy(policy_id_1).transact({'from': policy_owner, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
tx = policy_manager.functions.revokePolicy(policy_id_2).transact({'from': policy_owner, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
assert policy_manager.functions.policies(policy_id_1).call()[DISABLED_FIELD]
|
||||||
|
assert policy_manager.functions.policies(policy_id_2).call()[DISABLED_FIELD]
|
||||||
|
|
||||||
|
# Can't create policy with wrong ETH value - when fee is not calculated by formula:
|
||||||
|
# numberOfNodes * feeRate * numberOfPeriods * numberOfPolicies
|
||||||
|
policy_id_1 = os.urandom(POLICY_ID_LENGTH)
|
||||||
|
policy_id_2 = os.urandom(POLICY_ID_LENGTH)
|
||||||
|
policies = [policy_id_1, policy_id_2]
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = policy_manager.functions.createPolicies(policies, policy_sponsor, end_timestamp, [node1])\
|
||||||
|
.transact({'from': policy_sponsor, 'value': value - 1})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
min_rate, default_rate, max_rate = 10, 20, 30
|
||||||
|
tx = policy_manager.functions.setFeeRateRange(min_rate, default_rate, max_rate).transact({'from': creator})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
# Set minimum fee rate for nodes
|
||||||
|
tx = policy_manager.functions.setMinFeeRate(10).transact({'from': node1})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
tx = policy_manager.functions.setMinFeeRate(20).transact({'from': node2})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
assert policy_manager.functions.nodes(node1).call()[MIN_FEE_RATE_FIELD] == 10
|
||||||
|
assert policy_manager.functions.nodes(node2).call()[MIN_FEE_RATE_FIELD] == 20
|
||||||
|
assert policy_manager.functions.getMinFeeRate(node1).call() == 10
|
||||||
|
assert policy_manager.functions.getMinFeeRate(node2).call() == 20
|
||||||
|
|
||||||
|
# Try to create policy with low rate
|
||||||
|
current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
|
||||||
|
end_timestamp = current_timestamp + 10
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = policy_manager.functions.createPolicies(policies, policy_sponsor, end_timestamp, [node1])\
|
||||||
|
.transact({'from': policy_sponsor, 'value': 2 * (min_rate - 1)})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = policy_manager.functions.createPolicies(policies, policy_sponsor, end_timestamp, [node1, node2])\
|
||||||
|
.transact({'from': policy_sponsor, 'value': 2 * 2 * (min_rate + 1)})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
# Create new policy
|
||||||
|
value = 2 * default_rate * number_of_periods
|
||||||
|
end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
|
||||||
|
tx = policy_manager.functions.createPolicies(
|
||||||
|
policies, NULL_ADDRESS, end_timestamp, [node1, node2]) \
|
||||||
|
.transact({'from': policy_sponsor, 'value': 2 * value, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
|
||||||
|
assert 2 * value == testerchain.client.get_balance(policy_manager.address)
|
||||||
|
assert policy_sponsor_balance - 2 * value == testerchain.client.get_balance(policy_sponsor)
|
||||||
|
events = policy_created_log.get_all_entries()
|
||||||
|
assert len(events) == 6
|
||||||
|
|
||||||
|
for i, policy_id in enumerate(policies):
|
||||||
|
policy = policy_manager.functions.policies(policy_id).call()
|
||||||
|
assert policy_sponsor == policy[SPONSOR_FIELD]
|
||||||
|
assert NULL_ADDRESS == policy[OWNER_FIELD]
|
||||||
|
assert default_rate == policy[RATE_FIELD]
|
||||||
|
assert current_timestamp == policy[START_TIMESTAMP_FIELD]
|
||||||
|
assert end_timestamp == policy[END_TIMESTAMP_FIELD]
|
||||||
|
assert not policy[DISABLED_FIELD]
|
||||||
|
assert policy_sponsor == policy_manager.functions.getPolicyOwner(policy_id).call()
|
||||||
|
|
||||||
|
event_args = events[i + 4]['args']
|
||||||
|
assert policy_id == event_args['policyId']
|
||||||
|
assert policy_sponsor == event_args['sponsor']
|
||||||
|
assert policy_sponsor == event_args['owner']
|
||||||
|
assert rate == event_args['feeRate']
|
||||||
|
assert current_timestamp == event_args['startTimestamp']
|
||||||
|
assert end_timestamp == event_args['endTimestamp']
|
||||||
|
assert 2 == event_args['numberOfNodes']
|
||||||
|
|
||||||
|
|
||||||
def test_upgrading(testerchain, deploy_contract):
|
def test_upgrading(testerchain, deploy_contract):
|
||||||
creator = testerchain.client.accounts[0]
|
creator = testerchain.client.accounts[0]
|
||||||
|
|
||||||
|
|
|
@ -448,7 +448,26 @@ def estimate_gas(analyzer: AnalyzeGas = None) -> None:
|
||||||
policy_functions.revokePolicy(policy_id_3),
|
policy_functions.revokePolicy(policy_id_3),
|
||||||
{'from': alice2})
|
{'from': alice2})
|
||||||
|
|
||||||
for index in range(5):
|
transact(staker_functions.commitToNextPeriod(), {'from': staker1})
|
||||||
|
transact(staker_functions.commitToNextPeriod(), {'from': staker2})
|
||||||
|
transact(staker_functions.commitToNextPeriod(), {'from': staker3})
|
||||||
|
testerchain.time_travel(periods=1)
|
||||||
|
#
|
||||||
|
# Batch granting
|
||||||
|
#
|
||||||
|
policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
|
||||||
|
policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
|
||||||
|
current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
|
||||||
|
end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
|
||||||
|
value = 3 * number_of_periods * rate
|
||||||
|
transact_and_log("Creating 2 policies (3 nodes, 100 periods, pre-committed)",
|
||||||
|
policy_functions.createPolicies([policy_id_1, policy_id_2],
|
||||||
|
alice1,
|
||||||
|
end_timestamp,
|
||||||
|
[staker1, staker2, staker3]),
|
||||||
|
{'from': alice1, 'value': 2 * value})
|
||||||
|
|
||||||
|
for index in range(4):
|
||||||
transact(staker_functions.commitToNextPeriod(), {'from': staker1})
|
transact(staker_functions.commitToNextPeriod(), {'from': staker1})
|
||||||
testerchain.time_travel(periods=1)
|
testerchain.time_travel(periods=1)
|
||||||
transact(staker_functions.mint(), {'from': staker1})
|
transact(staker_functions.mint(), {'from': staker1})
|
||||||
|
|
Loading…
Reference in New Issue