Merge pull request #2204 from vzotova/optimize-it

One more time: `commitToNextPeriod` gas optimizations
pull/2357/head
K Prasch 2020-10-13 14:04:09 -07:00 committed by GitHub
commit 1ae88977aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 219 additions and 252 deletions

View File

@ -291,7 +291,7 @@ class BlockchainInterface:
self.log.debug(f"Currently, our gas strategy returns a gas price of {gwei_gas_price} gwei")
self.client.add_middleware(middleware.time_based_cache_middleware)
self.client.add_middleware(middleware.latest_block_based_cache_middleware)
# self.client.add_middleware(middleware.latest_block_based_cache_middleware)
self.client.add_middleware(middleware.simple_cache_middleware)
def connect(self):

View File

@ -15,4 +15,4 @@ You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
SOLIDITY_COMPILER_VERSION = 'v0.7.1'
SOLIDITY_COMPILER_VERSION = 'v0.7.3'

View File

@ -16,7 +16,7 @@ import "contracts/proxy/Upgradeable.sol";
/**
* @notice Contract holds policy data and locks accrued policy fees
* @dev |v6.1.2|
* @dev |v6.1.3|
*/
contract PolicyManager is Upgradeable {
using SafeERC20 for NuCypherToken;
@ -49,7 +49,6 @@ contract PolicyManager is Upgradeable {
);
event PolicyRevoked(bytes16 indexed policyId, address indexed sender, uint256 value);
event RefundForPolicy(bytes16 indexed policyId, address indexed sender, uint256 value);
event NodeBrokenState(address indexed node, uint16 period);
event MinFeeRateSet(address indexed node, uint256 value);
// TODO #1501
// Range range
@ -291,50 +290,57 @@ contract PolicyManager is Upgradeable {
}
/**
* @notice Set default `feeDelta` value for specified period
* @dev This method increases gas cost for node in trade of decreasing cost for policy sponsor
* @notice Call from StakingEscrow to update node info once per period.
* Set default `feeDelta` value for specified period and update node fee
* @param _node Node address
* @param _period Period to set
* @param _processedPeriod1 Processed period
* @param _processedPeriod2 Processed period
* @param _periodToSetDefault Period to set
*/
function setDefaultFeeDelta(address _node, uint16 _period) external onlyEscrowContract {
function ping(
address _node,
uint16 _processedPeriod1,
uint16 _processedPeriod2,
uint16 _periodToSetDefault
)
external onlyEscrowContract
{
NodeInfo storage node = nodes[_node];
if (node.feeDelta[_period] == 0) {
node.feeDelta[_period] = DEFAULT_FEE_DELTA;
if (_processedPeriod1 != 0) {
updateFee(node, _processedPeriod1);
}
if (_processedPeriod2 != 0) {
updateFee(node, _processedPeriod2);
}
// This code increases gas cost for node in trade of decreasing cost for policy sponsor
if (_periodToSetDefault != 0 && node.feeDelta[_periodToSetDefault] == 0) {
node.feeDelta[_periodToSetDefault] = DEFAULT_FEE_DELTA;
}
}
/**
* @notice Update node fee
* @param _node Node address
* @param _info Node info structure
* @param _period Processed period
*/
function updateFee(address _node, uint16 _period) external onlyEscrowContract {
NodeInfo storage node = nodes[_node];
if (node.previousFeePeriod == 0 || _period <= node.previousFeePeriod) {
function updateFee(NodeInfo storage _info, uint16 _period) internal {
if (_info.previousFeePeriod == 0 || _period <= _info.previousFeePeriod) {
return;
}
for (uint16 i = node.previousFeePeriod + 1; i <= _period; i++) {
int256 delta = node.feeDelta[i];
for (uint16 i = _info.previousFeePeriod + 1; i <= _period; i++) {
int256 delta = _info.feeDelta[i];
if (delta == DEFAULT_FEE_DELTA) {
// gas refund
node.feeDelta[i] = 0;
_info.feeDelta[i] = 0;
continue;
}
// broken state
if (delta < 0 && uint256(-delta) > node.feeRate) {
node.feeDelta[i] += int256(node.feeRate);
node.feeRate = 0;
emit NodeBrokenState(_node, _period);
// good state
} else {
node.feeRate = node.feeRate.addSigned(delta);
// gas refund
node.feeDelta[i] = 0;
}
_info.feeRate = _info.feeRate.addSigned(delta);
// gas refund
_info.feeDelta[i] = 0;
}
node.previousFeePeriod = _period;
node.fee += uint128(node.feeRate);
_info.previousFeePeriod = _period;
_info.fee += uint128(_info.feeRate);
}
/**

View File

@ -16,9 +16,13 @@ import "zeppelin/token/ERC20/SafeERC20.sol";
*/
interface PolicyManagerInterface {
function register(address _node, uint16 _period) external;
function updateFee(address _node, uint16 _period) external;
function escrow() external view returns (address);
function setDefaultFeeDelta(address _node, uint16 _period) external;
function ping(
address _node,
uint16 _processedPeriod1,
uint16 _processedPeriod2,
uint16 _periodToSetDefault
) external;
}
@ -41,7 +45,7 @@ interface WorkLockInterface {
/**
* @notice Contract holds and locks stakers tokens.
* Each staker that locks their tokens will receive some compensation
* @dev |v5.4.3|
* @dev |v5.4.4|
*/
contract StakingEscrow is Issuer, IERC900History {
@ -540,7 +544,7 @@ contract StakingEscrow is Issuer, IERC900History {
uint256 _value,
uint16 _periods
)
external isInitialized
external
{
require(msg.sender == address(workLock));
StakerInfo storage info = stakerInfo[_staker];
@ -1086,7 +1090,7 @@ contract StakingEscrow is Issuer, IERC900History {
require(msg.sender == tx.origin);
uint16 lastCommittedPeriod = getLastCommittedPeriod(staker);
mint(staker);
(uint16 processedPeriod1, uint16 processedPeriod2) = mint(staker);
uint16 currentPeriod = getCurrentPeriod();
uint16 nextPeriod = currentPeriod + 1;
@ -1108,7 +1112,8 @@ contract StakingEscrow is Issuer, IERC900History {
if (lastCommittedPeriod < currentPeriod) {
info.pastDowntime.push(Downtime(lastCommittedPeriod + 1, currentPeriod));
}
policyManager.setDefaultFeeDelta(staker, nextPeriod);
policyManager.ping(staker, processedPeriod1, processedPeriod2, nextPeriod);
emit CommitmentMade(staker, nextPeriod, lockedTokens);
}
@ -1143,37 +1148,46 @@ contract StakingEscrow is Issuer, IERC900History {
if (info.nextCommittedPeriod <= previousPeriod && info.nextCommittedPeriod != 0) {
info.lastCommittedPeriod = info.nextCommittedPeriod;
}
mint(msg.sender);
(uint16 processedPeriod1, uint16 processedPeriod2) = mint(msg.sender);
if (processedPeriod1 != 0 || processedPeriod2 != 0) {
policyManager.ping(msg.sender, processedPeriod1, processedPeriod2, 0);
}
}
/**
* @notice Mint tokens for previous periods if staker locked their tokens and made a commitment
* @param _staker Staker
* @return processedPeriod1 Processed period: currentCommittedPeriod or zero
* @return processedPeriod2 Processed period: nextCommittedPeriod or zero
*/
function mint(address _staker) internal {
function mint(address _staker) internal returns (uint16 processedPeriod1, uint16 processedPeriod2) {
uint16 currentPeriod = getCurrentPeriod();
uint16 previousPeriod = currentPeriod - 1;
uint16 previousPeriod = currentPeriod - 1;
StakerInfo storage info = stakerInfo[_staker];
if (info.nextCommittedPeriod == 0 ||
info.currentCommittedPeriod == 0 &&
info.nextCommittedPeriod > previousPeriod ||
info.currentCommittedPeriod > previousPeriod) {
return;
return (0, 0);
}
uint16 startPeriod = getStartPeriod(info, currentPeriod);
uint256 reward = 0;
bool reStake = !info.flags.bitSet(RE_STAKE_DISABLED_INDEX);
if (info.currentCommittedPeriod != 0) {
reward = mint(_staker, info, info.currentCommittedPeriod, currentPeriod, startPeriod, reStake);
reward = mint(info, info.currentCommittedPeriod, currentPeriod, startPeriod, reStake);
processedPeriod1 = info.currentCommittedPeriod;
info.currentCommittedPeriod = 0;
if (reStake) {
lockedPerPeriod[info.nextCommittedPeriod] += reward;
}
}
if (info.nextCommittedPeriod <= previousPeriod) {
reward += mint(_staker, info, info.nextCommittedPeriod, currentPeriod, startPeriod, reStake);
reward += mint(info, info.nextCommittedPeriod, currentPeriod, startPeriod, reStake);
processedPeriod2 = info.nextCommittedPeriod;
info.nextCommittedPeriod = 0;
}
@ -1188,14 +1202,12 @@ contract StakingEscrow is Issuer, IERC900History {
/**
* @notice Calculate reward for one period
* @param _staker Staker's address
* @param _info Staker structure
* @param _mintingPeriod Period for minting calculation
* @param _currentPeriod Current period
* @param _startPeriod Pre-calculated start period
*/
function mint(
address _staker,
StakerInfo storage _info,
uint16 _mintingPeriod,
uint16 _currentPeriod,
@ -1220,7 +1232,6 @@ contract StakingEscrow is Issuer, IERC900History {
}
}
}
policyManager.updateFee(_staker, _mintingPeriod);
return reward;
}

View File

@ -86,24 +86,15 @@ contract StakingEscrowForPolicyMock {
}
/**
* @notice Emulate mint method
* @param _staker Staker on behalf of whom minting will be
* @param _startPeriod Start period for minting
* @param _numberOfPeriods Number periods for minting
* @notice Emulate ping method call
*/
function mint(address _staker, uint16 _startPeriod, uint16 _numberOfPeriods) public {
for (uint16 i = 0; i < _numberOfPeriods; i++) {
policyManager.updateFee(_staker, i + _startPeriod);
}
}
/**
* @notice Emulate mint method
* @param _startPeriod Start period for minting
* @param _numberOfPeriods Number periods for minting
*/
function mint(uint16 _startPeriod, uint16 _numberOfPeriods) external {
mint(msg.sender, _startPeriod, _numberOfPeriods);
function ping(
address _node,
uint16 _processedPeriod1,
uint16 _processedPeriod2,
uint16 _periodToSetDefault
) external {
policyManager.ping(_node, _processedPeriod1, _processedPeriod2, _periodToSetDefault);
}
/**
@ -145,12 +136,6 @@ contract StakingEscrowForPolicyMock {
}
}
function setDefaultFeeDelta(address _node, uint16 _startPeriod, uint16 _numberOfPeriods) public {
for (uint16 i = 0; i < _numberOfPeriods; i++) {
policyManager.setDefaultFeeDelta(_node, i + _startPeriod);
}
}
}

View File

@ -181,12 +181,15 @@ contract PolicyManagerForStakingEscrowMock {
nodes[_node].push(_period);
}
function updateFee(address _node, uint16 _period) external {
nodes[_node].push(_period);
}
function setDefaultFeeDelta(address _node, uint16 _period) external {
nodes[_node].push(_period);
function ping(
address _node,
uint16 _processedPeriod1,
uint16 _processedPeriod2,
uint16 _periodToSetDefault
) external {
nodes[_node].push(_processedPeriod1);
nodes[_node].push(_processedPeriod2);
nodes[_node].push(_periodToSetDefault);
}
function getPeriodsLength(address _node) public view returns (uint256) {

View File

@ -569,23 +569,7 @@ def test_worklock_phases(testerchain,
testerchain.wait_for_receipt(tx)
assert worklock.functions.nextBidderToCheck().call() == 2
# Can't claim before initialization
with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.claim().transact({'from': staker2, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
# Initialize escrow
tx = token.functions.transfer(multisig.address, token_economics.erc20_reward_supply).transact({'from': creator})
testerchain.wait_for_receipt(tx)
tx = token.functions.approve(escrow.address, token_economics.erc20_reward_supply) \
.buildTransaction({'from': multisig.address, 'gasPrice': 0})
execute_multisig_transaction(testerchain, multisig, [contracts_owners[0], contracts_owners[1]], tx)
tx = escrow.functions.initialize(token_economics.erc20_reward_supply, multisig.address) \
.buildTransaction({'from': multisig.address, 'gasPrice': 0})
execute_multisig_transaction(testerchain, multisig, [contracts_owners[0], contracts_owners[1]], tx)
pytest.escrow_supply += token_economics.erc20_reward_supply
# Stakers claim tokens
# Stakers claim tokens before initialization
assert not worklock.functions.workInfo(staker2).call()[2]
tx = worklock.functions.claim().transact({'from': staker2, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
@ -611,6 +595,17 @@ def test_worklock_phases(testerchain,
wind_down, _re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker2).call()
assert wind_down
# Initialize escrow
tx = token.functions.transfer(multisig.address, token_economics.erc20_reward_supply).transact({'from': creator})
testerchain.wait_for_receipt(tx)
tx = token.functions.approve(escrow.address, token_economics.erc20_reward_supply) \
.buildTransaction({'from': multisig.address, 'gasPrice': 0})
execute_multisig_transaction(testerchain, multisig, [contracts_owners[0], contracts_owners[1]], tx)
tx = escrow.functions.initialize(token_economics.erc20_reward_supply, multisig.address) \
.buildTransaction({'from': multisig.address, 'gasPrice': 0})
execute_multisig_transaction(testerchain, multisig, [contracts_owners[0], contracts_owners[1]], tx)
pytest.escrow_supply += token_economics.erc20_reward_supply
assert not escrow.functions.isReStakeLocked(staker1).call()
tx = worklock.functions.claim().transact({'from': staker1, 'gas_price': 0})
testerchain.wait_for_receipt(tx)

View File

@ -54,7 +54,6 @@ def test_create_revoke(testerchain, escrow, policy_manager):
policy_revoked_log = policy_manager.events.PolicyRevoked.createFilter(fromBlock='latest')
arrangement_refund_log = policy_manager.events.RefundForArrangement.createFilter(fromBlock='latest')
policy_refund_log = policy_manager.events.RefundForPolicy.createFilter(fromBlock='latest')
warn_log = policy_manager.events.NodeBrokenState.createFilter(fromBlock='latest')
min_fee_log = policy_manager.events.MinFeeRateSet.createFilter(fromBlock='latest')
fee_range_log = policy_manager.events.FeeRateRangeSet.createFilter(fromBlock='latest')
@ -182,10 +181,12 @@ def test_create_revoke(testerchain, escrow, policy_manager):
# Create new policy
period = escrow.functions.getCurrentPeriod().call()
tx = escrow.functions.setDefaultFeeDelta(node1, period, number_of_periods + 1).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.setDefaultFeeDelta(node2, period, number_of_periods + 1).transact()
testerchain.wait_for_receipt(tx)
for period_to_set_default in range(period, period + number_of_periods + 1):
tx = escrow.functions.ping(node1, 0, 0, period_to_set_default).transact()
testerchain.wait_for_receipt(tx)
for period_to_set_default in range(period, period + number_of_periods + 1):
tx = escrow.functions.ping(node2, 0, 0, period_to_set_default).transact()
testerchain.wait_for_receipt(tx)
end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
policy_id_2 = os.urandom(POLICY_ID_LENGTH)
tx = policy_manager.functions.createPolicy(policy_id_2, policy_owner, end_timestamp, [node1, node2, node3])\
@ -477,8 +478,6 @@ def test_create_revoke(testerchain, escrow, policy_manager):
events = policy_refund_log.get_all_entries()
assert 0 == len(events)
assert len(warn_log.get_all_entries()) == 0
# If min fee rate is outside of the range after changing it - then default value must be returned
min_rate, default_rate, max_rate = 11, 15, 19
tx = policy_manager.functions.setFeeRateRange(min_rate, default_rate, max_rate).transact({'from': creator})
@ -621,8 +620,6 @@ def test_handling_wrong_state(testerchain, deploy_contract):
tx = escrow.functions.setPolicyManager(policy_manager.address).transact({'from': creator})
testerchain.wait_for_receipt(tx)
warn_log = policy_manager.events.NodeBrokenState.createFilter(fromBlock='latest')
current_period = policy_manager.functions.getCurrentPeriod().call()
initial_period = current_period - 1
tx = escrow.functions.register(node1, initial_period).transact()
@ -634,75 +631,16 @@ def test_handling_wrong_state(testerchain, deploy_contract):
testerchain.wait_for_receipt(tx)
tx = policy_manager.functions.setNodeFeeDelta(node1, initial_period + number_of_periods, -1).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.setDefaultFeeDelta(node1, current_period, 1).transact()
tx = escrow.functions.ping(node1, 0, 0, current_period).transact()
testerchain.wait_for_receipt(tx)
# Emulate making a commitments
for i in range(1, number_of_periods + 2):
testerchain.time_travel(hours=1)
current_period = policy_manager.functions.getCurrentPeriod().call()
tx = escrow.functions.setDefaultFeeDelta(node1, current_period + 1, 1).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.mint(current_period - 1, 1).transact({'from': node1})
testerchain.wait_for_receipt(tx)
fee, previous_fee_period, fee_rate, _min_fee_rate = policy_manager.functions.nodes(node1).call()
assert fee == 0
assert fee_rate == 0
assert previous_fee_period == current_period - 1
assert policy_manager.functions.getNodeFeeDelta(node1, initial_period).call() == 1
for i in range(1, number_of_periods):
assert policy_manager.functions.getNodeFeeDelta(node1, initial_period + i).call() == 0
assert policy_manager.functions.getNodeFeeDelta(node1, initial_period + number_of_periods).call() == -1
events = warn_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert event_args['node'] == node1
assert event_args['period'] == initial_period + number_of_periods
# Same case but with more diverse values
testerchain.time_travel(hours=1)
current_period = policy_manager.functions.getCurrentPeriod().call()
initial_period = current_period - 1
tx = escrow.functions.register(node2, initial_period).transact()
tx = escrow.functions.ping(node1, 0, current_period - 1, current_period + 1).transact()
testerchain.wait_for_receipt(tx)
number_of_periods = 5
tx = escrow.functions.setDefaultFeeDelta(node2, current_period, 1).transact()
testerchain.wait_for_receipt(tx)
tx = policy_manager.functions.setNodeFeeDelta(node2, initial_period, 100).transact()
testerchain.wait_for_receipt(tx)
tx = policy_manager.functions.setNodeFeeDelta(node2, initial_period + number_of_periods, -100).transact()
testerchain.wait_for_receipt(tx)
tx = policy_manager.functions.setNodeFeeDelta(node2, initial_period + 2, 50).transact()
testerchain.wait_for_receipt(tx)
tx = policy_manager.functions.setNodeFeeDelta(node2, initial_period + 2 + number_of_periods, -50).transact()
testerchain.wait_for_receipt(tx)
# Emulate making a commitments
for i in range(1, number_of_periods + 4):
testerchain.time_travel(hours=1)
current_period = policy_manager.functions.getCurrentPeriod().call()
tx = escrow.functions.setDefaultFeeDelta(node2, current_period + 1, 1).transact()
testerchain.time_travel(hours=1)
current_period = policy_manager.functions.getCurrentPeriod().call()
with pytest.raises((TransactionFailed, ValueError)):
tx = escrow.functions.ping(node1, 0, current_period - 1, current_period + 1).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.mint(current_period - 1, 1).transact({'from': node2})
testerchain.wait_for_receipt(tx)
fee, previous_fee_period, fee_rate, _min_fee_rate = policy_manager.functions.nodes(node2).call()
assert fee == 50 * (number_of_periods - 2)
assert fee_rate == 0
assert previous_fee_period == current_period - 1
assert policy_manager.functions.getNodeFeeDelta(node2, initial_period).call() == 100
for i in range(1, number_of_periods - 2):
assert policy_manager.functions.getNodeFeeDelta(node2, initial_period + i).call() == 0
assert policy_manager.functions.getNodeFeeDelta(node2, initial_period + number_of_periods).call() == -50
assert policy_manager.functions.getNodeFeeDelta(node2, initial_period + number_of_periods + 1).call() == 0
assert policy_manager.functions.getNodeFeeDelta(node2, initial_period + number_of_periods + 2).call() == -50
events = warn_log.get_all_entries()
assert 3 == len(events)
event_args = events[1]['args']
assert event_args['node'] == node2
assert event_args['period'] == initial_period + number_of_periods
event_args = events[2]['args']
assert event_args['node'] == node2
assert event_args['period'] == initial_period + number_of_periods + 2

View File

@ -41,17 +41,18 @@ def test_fee(testerchain, escrow, policy_manager):
creator, policy_sponsor, bad_node, node1, node2, node3, *everyone_else = testerchain.client.accounts
node_balance = testerchain.client.get_balance(node1)
withdraw_log = policy_manager.events.Withdrawn.createFilter(fromBlock='latest')
warn_log = policy_manager.events.NodeBrokenState.createFilter(fromBlock='latest')
# Mint period without policies
# Emulate minting period without policies
period = escrow.functions.getCurrentPeriod().call()
tx = escrow.functions.mint(period - 1, 1).transact({'from': node1, 'gas_price': 0})
tx = escrow.functions.ping(node1, period - 1, 0, period - 1).transact()
testerchain.wait_for_receipt(tx)
assert 0 == policy_manager.functions.nodes(node1).call()[FEE_FIELD]
for period_to_set_default in range(period, period + number_of_periods + 1):
tx = escrow.functions.ping(node1, 0, 0, period_to_set_default).transact()
testerchain.wait_for_receipt(tx)
# Create policy
tx = escrow.functions.setDefaultFeeDelta(node1, period - 1, number_of_periods + 2).transact()
testerchain.wait_for_receipt(tx)
current_timestamp = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp
end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
tx = policy_manager.functions.createPolicy(policy_id, policy_sponsor, end_timestamp, [node1, node3])\
@ -63,22 +64,19 @@ def test_fee(testerchain, escrow, policy_manager):
tx = policy_manager.functions.withdraw().transact({'from': node1})
testerchain.wait_for_receipt(tx)
# Can't update fee directly (only through mint method in the escrow contract)
# Can't ping directly (only through mint/commitToNextPeriod methods in the escrow contract)
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.updateFee(node1, period + 1).transact({'from': node1})
tx = policy_manager.functions.ping(node1, period + 1, 0, period).transact({'from': node1})
testerchain.wait_for_receipt(tx)
# Can't register directly (only through deposit method in the escrow contract)
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.register(bad_node, period).transact({'from': bad_node})
testerchain.wait_for_receipt(tx)
# Can't set default value directly (only through commitToNextPeriod method in the escrow contract)
with pytest.raises((TransactionFailed, ValueError)):
tx = policy_manager.functions.setDefaultFeeDelta(bad_node, period).transact({'from': bad_node})
testerchain.wait_for_receipt(tx)
# Mint some periods for calling updateFee method
tx = escrow.functions.mint(period - 1, 5).transact({'from': node1, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
for minting_period in range(period - 1, period + 4):
tx = escrow.functions.ping(node1, minting_period, 0, 0).transact()
testerchain.wait_for_receipt(tx)
period += 4
assert 80 == policy_manager.functions.nodes(node1).call()[FEE_FIELD]
@ -96,13 +94,25 @@ def test_fee(testerchain, escrow, policy_manager):
assert 80 == event_args['value']
# Mint more periods
tx = escrow.functions.setDefaultFeeDelta(node1, period, 6).transact()
tx = escrow.functions.ping(node1, 0, 0, period).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.mint(period, 6).transact({'from': node1, 'gas_price': 0})
tx = escrow.functions.ping(node1, 0, 0, period + 1).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.ping(node1, 0, 0, period + 2).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.ping(node1, period, period + 1, period + 3).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.ping(node1, period + 2, 0, period + 4).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.ping(node1, 0, period + 3, period + 5).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.ping(node1, period + 4, period + 5, 0).transact()
testerchain.wait_for_receipt(tx)
period += 6
assert 120 == policy_manager.functions.nodes(node1).call()[FEE_FIELD]
tx = escrow.functions.mint(period, 1).transact({'from': node1, 'gas_price': 0})
tx = escrow.functions.ping(node1, period, 0, 0).transact()
testerchain.wait_for_receipt(tx)
assert 120 == policy_manager.functions.nodes(node1).call()[FEE_FIELD]
@ -121,21 +131,23 @@ def test_fee(testerchain, escrow, policy_manager):
# Create policy
period = escrow.functions.getCurrentPeriod().call()
tx = escrow.functions.setDefaultFeeDelta(node1, period, 1).transact()
tx = escrow.functions.ping(node1, 0, 0, period).transact()
testerchain.wait_for_receipt(tx)
tx = policy_manager.functions.createPolicy(policy_id_2, policy_sponsor, end_timestamp, [node2, node3]) \
.transact({'from': policy_sponsor, 'value': int(2 * value)})
testerchain.wait_for_receipt(tx)
# Mint some periods
tx = escrow.functions.mint(period, 5).transact({'from': node2, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
for minting_period in range(period, period + 5):
tx = escrow.functions.ping(node2, 0, minting_period, 0).transact()
testerchain.wait_for_receipt(tx)
period += 5
assert 100 == policy_manager.functions.nodes(node2).call()[FEE_FIELD]
# Mint more periods
tx = escrow.functions.mint(period, 6).transact({'from': node2, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
for minting_period in range(period, period + 6):
tx = escrow.functions.ping(node2, 0, minting_period, 0).transact()
testerchain.wait_for_receipt(tx)
period += 6
assert 200 == policy_manager.functions.nodes(node2).call()[FEE_FIELD]
@ -155,8 +167,6 @@ def test_fee(testerchain, escrow, policy_manager):
assert node1 == event_args['recipient']
assert 200 == event_args['value']
assert len(warn_log.get_all_entries()) == 0
def test_refund(testerchain, escrow, policy_manager):
creator, policy_creator, bad_node, node1, node2, node3, policy_owner, *everyone_else = testerchain.client.accounts
@ -167,7 +177,6 @@ def test_refund(testerchain, escrow, policy_manager):
policy_revoked_log = policy_manager.events.PolicyRevoked.createFilter(fromBlock='latest')
arrangement_refund_log = policy_manager.events.RefundForArrangement.createFilter(fromBlock='latest')
policy_refund_log = policy_manager.events.RefundForPolicy.createFilter(fromBlock='latest')
warn_log = policy_manager.events.NodeBrokenState.createFilter(fromBlock='latest')
# Create policy
current_timestamp = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp
@ -327,18 +336,18 @@ def test_refund(testerchain, escrow, policy_manager):
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})
tx = escrow.functions.ping(node1, 0, period, 0).transact()
testerchain.wait_for_receipt(tx)
period += 1
tx = escrow.functions.mint(period, 2).transact({'from': node1})
tx = escrow.functions.ping(node1, period, period + 1, 0).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.pushDowntimePeriod(period + 2, period + 3).transact({'from': creator})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.mint(period + 4, 1).transact({'from': node1})
tx = escrow.functions.ping(node1, period + 4, 0, 0).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.pushDowntimePeriod(period + 5, period + 7).transact({'from': creator})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.mint(period + 8, 1).transact({'from': node1})
tx = escrow.functions.ping(node1, period + 8, 0, 0).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.setLastCommittedPeriod(period + 8).transact({'from': creator})
testerchain.wait_for_receipt(tx)
@ -429,7 +438,9 @@ def test_refund(testerchain, escrow, policy_manager):
period += 1
tx = escrow.functions.pushDowntimePeriod(period - 1, period).transact({'from': creator})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.mint(period + 1, 3).transact({'from': node1})
tx = escrow.functions.ping(node1, period + 1, period + 2, 0).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.ping(node1, period + 3, 0, 0).transact()
testerchain.wait_for_receipt(tx)
period += 3
tx = escrow.functions.setLastCommittedPeriod(period).transact({'from': creator})
@ -444,8 +455,9 @@ def test_refund(testerchain, escrow, policy_manager):
policy_manager_balance = testerchain.client.get_balance(policy_manager.address)
creator_balance = testerchain.client.get_balance(policy_creator)
period = escrow.functions.getCurrentPeriod().call()
tx = escrow.functions.setDefaultFeeDelta(node1, period + 1, number_of_periods).transact()
testerchain.wait_for_receipt(tx)
for period_to_set_default in range(period + 1, period + number_of_periods):
tx = escrow.functions.ping(node1, 0, 0, period_to_set_default).transact()
testerchain.wait_for_receipt(tx)
tx = policy_manager.functions.revokePolicy(policy_id_3).transact({'from': policy_creator, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
returned = 40 + 5 * rate
@ -474,8 +486,9 @@ def test_refund(testerchain, escrow, policy_manager):
assert returned == event_args['value']
# Minting is useless after policy is revoked
tx = escrow.functions.mint(period + 1, number_of_periods + 1).transact({'from': node1})
testerchain.wait_for_receipt(tx)
for minting_period in range(period + 1, period + number_of_periods + 1):
tx = escrow.functions.ping(node1, 0, minting_period, 0).transact()
testerchain.wait_for_receipt(tx)
period += 20
assert 160 == policy_manager.functions.nodes(node1).call()[FEE_FIELD]
@ -537,8 +550,6 @@ def test_refund(testerchain, escrow, policy_manager):
events = policy_created_log.get_all_entries()
assert 4 == len(events)
assert len(warn_log.get_all_entries()) == 0
def test_reentrancy(testerchain, escrow, policy_manager, deploy_contract):
withdraw_log = policy_manager.events.Withdrawn.createFilter(fromBlock='latest')
@ -546,7 +557,6 @@ def test_reentrancy(testerchain, escrow, policy_manager, deploy_contract):
policy_revoked_log = policy_manager.events.PolicyRevoked.createFilter(fromBlock='latest')
arrangement_refund_log = policy_manager.events.RefundForArrangement.createFilter(fromBlock='latest')
policy_refund_log = policy_manager.events.RefundForPolicy.createFilter(fromBlock='latest')
warn_log = policy_manager.events.NodeBrokenState.createFilter(fromBlock='latest')
reentrancy_contract, _ = deploy_contract('ReentrancyTest')
contract_address = reentrancy_contract.address
@ -574,7 +584,7 @@ def test_reentrancy(testerchain, escrow, policy_manager, deploy_contract):
testerchain.time_travel(hours=1)
period = escrow.functions.getCurrentPeriod().call()
tx = escrow.functions.mint(contract_address, period, 1).transact({'gas_price': 0})
tx = escrow.functions.ping(contract_address, 0, period, 0).transact()
testerchain.wait_for_receipt(tx)
assert 2 * rate == policy_manager.functions.nodes(contract_address).call()[FEE_FIELD]
@ -607,5 +617,3 @@ def test_reentrancy(testerchain, escrow, policy_manager, deploy_contract):
assert 0 == len(policy_revoked_log.get_all_entries())
assert 0 == len(arrangement_refund_log.get_all_entries())
assert 0 == len(policy_refund_log.get_all_entries())
assert len(warn_log.get_all_entries()) == 0

View File

@ -105,12 +105,16 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
assert 0 == escrow.functions.findIndexOfPastDowntime(staker2, 0).call()
assert 1 == escrow.functions.findIndexOfPastDowntime(staker2, current_period + 1).call()
# Check parameters in call of the policy manager mock
assert 2 == policy_manager.functions.getPeriodsLength(staker1).call()
assert 2 == policy_manager.functions.getPeriodsLength(staker2).call()
assert current_period - 1 == policy_manager.functions.getPeriod(staker1, 0).call()
assert current_period - 1 == policy_manager.functions.getPeriod(staker2, 0).call()
assert current_period + 1 == policy_manager.functions.getPeriod(staker1, 1).call()
assert current_period + 1 == policy_manager.functions.getPeriod(staker2, 1).call()
assert policy_manager.functions.getPeriodsLength(staker1).call() == 4
assert policy_manager.functions.getPeriodsLength(staker2).call() == 4
assert policy_manager.functions.getPeriod(staker1, 0).call() == current_period - 1
assert policy_manager.functions.getPeriod(staker2, 0).call() == current_period - 1
assert policy_manager.functions.getPeriod(staker1, 1).call() == 0
assert policy_manager.functions.getPeriod(staker2, 1).call() == 0
assert policy_manager.functions.getPeriod(staker1, 2).call() == 0
assert policy_manager.functions.getPeriod(staker2, 2).call() == 0
assert policy_manager.functions.getPeriod(staker1, 3).call() == current_period + 1
assert policy_manager.functions.getPeriod(staker2, 3).call() == current_period + 1
# Check downtime parameters
assert 1 == escrow.functions.getPastDowntimeLength(staker1).call()
downtime = escrow.functions.getPastDowntime(staker1, 0).call()
@ -141,13 +145,15 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
testerchain.wait_for_receipt(tx)
current_period = escrow.functions.getCurrentPeriod().call()
assert 1 == escrow.functions.getPastDowntimeLength(staker1).call()
assert 3 == policy_manager.functions.getPeriodsLength(staker1).call()
assert current_period + 1 == policy_manager.functions.getPeriod(staker1, 2).call()
assert policy_manager.functions.getPeriodsLength(staker1).call() == 7
assert policy_manager.functions.getPeriod(staker1, 4).call() == 0
assert policy_manager.functions.getPeriod(staker1, 5).call() == 0
assert policy_manager.functions.getPeriod(staker1, 6).call() == current_period + 1
# Checks that no error from repeated method call
tx = escrow.functions.commitToNextPeriod().transact({'from': staker1})
testerchain.wait_for_receipt(tx)
assert 3 == policy_manager.functions.getPeriodsLength(staker1).call()
assert policy_manager.functions.getPeriodsLength(staker1).call() == 7
# Staker and Staker(2) mint tokens for last periods
# And only Staker make a commitment to next period
@ -155,13 +161,18 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
tx = escrow.functions.commitToNextPeriod().transact({'from': staker1})
testerchain.wait_for_receipt(tx)
current_period = escrow.functions.getCurrentPeriod().call()
assert 5 == policy_manager.functions.getPeriodsLength(staker1).call()
assert current_period + 1 == policy_manager.functions.getPeriod(staker1, 4).call()
assert policy_manager.functions.getPeriodsLength(staker1).call() == 10
assert policy_manager.functions.getPeriod(staker1, 7).call() == current_period - 1
assert policy_manager.functions.getPeriod(staker1, 8).call() == 0
assert policy_manager.functions.getPeriod(staker1, 9).call() == current_period + 1
tx = escrow.functions.mint().transact({'from': staker2})
testerchain.wait_for_receipt(tx)
assert policy_manager.functions.getPeriodsLength(staker2).call() == 7
assert policy_manager.functions.getPeriod(staker2, 4).call() == 0
assert policy_manager.functions.getPeriod(staker2, 5).call() == current_period - 1
assert policy_manager.functions.getPeriod(staker2, 6).call() == 0
current_period = escrow.functions.getCurrentPeriod().call()
# Check result of minting
total_locked = staker1_stake + staker2_stake
ursula1_reward = calculate_reward(500, total_locked, 1) + calculate_reward(500, total_locked, 2)
@ -185,13 +196,6 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
assert ursula2_reward == event_args['value']
assert escrow.functions.getCurrentPeriod().call() - 1 == event_args['period']
# Check parameters in call of the policy manager mock
assert 5 == policy_manager.functions.getPeriodsLength(staker1).call()
assert 3 == policy_manager.functions.getPeriodsLength(staker2).call()
current_period = escrow.functions.getCurrentPeriod().call() - 1
assert current_period == policy_manager.functions.getPeriod(staker1, 3).call()
assert current_period == policy_manager.functions.getPeriod(staker2, 2).call()
# Staker tries to mint again and doesn't receive a reward
# There are no more committed periods that are ready to mint
staker1_stake += ursula1_reward
@ -201,6 +205,7 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
assert staker1_stake == escrow.functions.getAllTokens(staker1).call()
events = staking_log.get_all_entries()
assert 2 == len(events)
assert policy_manager.functions.getPeriodsLength(staker1).call() == 10
# Staker can't make a commitment to next period because stake is unlocked in current period
testerchain.time_travel(hours=1)
@ -220,6 +225,11 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
assert current_period - 1 == downtime[0]
assert current_period == downtime[1]
assert policy_manager.functions.getPeriodsLength(staker2).call() == 10
assert policy_manager.functions.getPeriod(staker2, 7).call() == 0
assert policy_manager.functions.getPeriod(staker2, 8).call() == 0
assert policy_manager.functions.getPeriod(staker2, 9).call() == current_period + 1
# Staker mints tokens
testerchain.time_travel(hours=1)
tx = escrow.functions.mint().transact({'from': staker1})
@ -239,10 +249,12 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
assert ursula1_reward == event_args['value']
assert current_period == event_args['period']
assert 7 == policy_manager.functions.getPeriodsLength(staker1).call()
assert 4 == policy_manager.functions.getPeriodsLength(staker2).call()
assert current_period - 1 == policy_manager.functions.getPeriod(staker1, 5).call()
assert current_period == policy_manager.functions.getPeriod(staker1, 6).call()
current_period = escrow.functions.getCurrentPeriod().call()
assert policy_manager.functions.getPeriodsLength(staker2).call() == 10
assert policy_manager.functions.getPeriodsLength(staker1).call() == 13
assert policy_manager.functions.getPeriod(staker1, 10).call() == current_period - 2
assert policy_manager.functions.getPeriod(staker1, 11).call() == current_period - 1
assert policy_manager.functions.getPeriod(staker1, 12).call() == 0
# Staker(2) mints tokens
testerchain.time_travel(hours=1)
@ -261,10 +273,12 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
assert ursula2_reward == event_args['value']
assert escrow.functions.getCurrentPeriod().call() - 1 == event_args['period']
current_period = escrow.functions.getCurrentPeriod().call() - 1
assert 7 == policy_manager.functions.getPeriodsLength(staker1).call()
assert 5 == policy_manager.functions.getPeriodsLength(staker2).call()
assert current_period == policy_manager.functions.getPeriod(staker2, 3).call()
current_period = escrow.functions.getCurrentPeriod().call()
assert policy_manager.functions.getPeriodsLength(staker1).call() == 13
assert policy_manager.functions.getPeriodsLength(staker2).call() == 13
assert policy_manager.functions.getPeriod(staker2, 10).call() == 0
assert policy_manager.functions.getPeriod(staker2, 11).call() == current_period - 1
assert policy_manager.functions.getPeriod(staker2, 12).call() == 0
# Staker(2) can't make a commitment because stake is unlocked
with pytest.raises((TransactionFailed, ValueError)):
@ -282,6 +296,8 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
tx = escrow.functions.commitToNextPeriod().transact({'from': staker1})
testerchain.wait_for_receipt(tx)
assert policy_manager.functions.getPeriodsLength(staker1).call() == 13
# Staker(2) deposits and locks more tokens
tx = escrow.functions.deposit(staker2, 250, 4).transact({'from': staker2})
testerchain.wait_for_receipt(tx)
@ -317,8 +333,10 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
assert current_period == downtime[1]
staker2_stake += ursula2_reward
assert 8 == policy_manager.functions.getPeriodsLength(staker2).call()
assert current_period - 4 == policy_manager.functions.getPeriod(staker2, 6).call()
assert policy_manager.functions.getPeriodsLength(staker2).call() == 19
assert policy_manager.functions.getPeriod(staker2, 16).call() == 0
assert policy_manager.functions.getPeriod(staker2, 17).call() == current_period - 4
assert policy_manager.functions.getPeriod(staker2, 18).call() == current_period + 1
events = staking_log.get_all_entries()
assert 5 == len(events)
@ -332,9 +350,14 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
tx = escrow.functions.commitToNextPeriod().transact({'from': staker2})
testerchain.wait_for_receipt(tx)
assert 4 == escrow.functions.getPastDowntimeLength(staker2).call()
testerchain.time_travel(hours=1)
testerchain.time_travel(hours=2)
current_period = escrow.functions.getCurrentPeriod().call()
tx = escrow.functions.commitToNextPeriod().transact({'from': staker2})
testerchain.wait_for_receipt(tx)
assert policy_manager.functions.getPeriodsLength(staker2).call() == 25
assert policy_manager.functions.getPeriod(staker2, 22).call() == current_period - 2
assert policy_manager.functions.getPeriod(staker2, 23).call() == current_period - 1
assert policy_manager.functions.getPeriod(staker2, 24).call() == current_period + 1
# Staker(2) withdraws all
testerchain.time_travel(hours=2)
@ -401,16 +424,18 @@ def test_minting(testerchain, token, escrow_contract, token_economics):
# Check searching downtime index
current_period = escrow.functions.getCurrentPeriod().call()
assert 0 == escrow.functions.findIndexOfPastDowntime(staker2, 0).call()
assert 0 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 14).call()
assert 1 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 13).call()
assert 1 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 11).call()
assert 0 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 15).call()
assert 1 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 14).call()
assert 1 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 12).call()
assert 2 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 11).call()
assert 2 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 10).call()
assert 2 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 9).call()
assert 3 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 8).call()
assert 3 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 4).call()
assert 4 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 3).call()
assert 4 == escrow.functions.findIndexOfPastDowntime(staker2, current_period).call()
assert 4 == escrow.functions.findIndexOfPastDowntime(staker2, current_period + 100).call()
assert 3 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 9).call()
assert 3 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 5).call()
assert 4 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 4).call()
assert 4 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 2).call()
assert 5 == escrow.functions.findIndexOfPastDowntime(staker2, current_period - 1).call()
assert 5 == escrow.functions.findIndexOfPastDowntime(staker2, current_period).call()
assert 5 == escrow.functions.findIndexOfPastDowntime(staker2, current_period + 100).call()
def test_slashing(testerchain, token, escrow_contract, token_economics, deploy_contract):

View File

@ -1583,14 +1583,7 @@ def test_staking_from_worklock(testerchain, token, escrow_contract, token_econom
testerchain.wait_for_receipt(tx)
assert token.functions.balanceOf(escrow.address).call() == 0
# Can't claim before initialization
with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.depositFromWorkLock(staker1, value, duration).transact()
testerchain.wait_for_receipt(tx)
tx = escrow.functions.initialize(0, creator).transact({'from': creator})
testerchain.wait_for_receipt(tx)
# Deposit tokens from WorkLock
# Can claim before initialization
wind_down, _re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker1).call()
assert not wind_down
current_period = escrow.functions.getCurrentPeriod().call()
@ -1634,6 +1627,9 @@ def test_staking_from_worklock(testerchain, token, escrow_contract, token_econom
tx = escrow.functions.deposit(staker2, value, duration).transact({'from': staker2})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.initialize(0, creator).transact({'from': creator})
testerchain.wait_for_receipt(tx)
wind_down, _re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker2).call()
assert not wind_down
current_period = escrow.functions.getCurrentPeriod().call()