SimplePREApplication: worker -> operator

pull/2861/head
vzotova 2022-01-25 10:51:55 +03:00 committed by Kieran Prasch
parent e461f2ff52
commit 33df782883
3 changed files with 185 additions and 183 deletions

View File

@ -13,46 +13,46 @@ import "threshold/IStaking.sol";
contract SimplePREApplication {
/**
* @notice Signals that a worker was bonded to the staking provider
* @notice Signals that an operator was bonded to the staking provider
* @param stakingProvider Staking provider address
* @param worker Worker address
* @param operator Operator address
* @param startTimestamp Timestamp bonding occurred
*/
event WorkerBonded(address indexed stakingProvider, address indexed worker, uint256 startTimestamp);
event OperatorBonded(address indexed stakingProvider, address indexed operator, uint256 startTimestamp);
/**
* @notice Signals that a worker address is confirmed
* @notice Signals that an operator address is confirmed
* @param stakingProvider Staking provider address
* @param worker Worker address
* @param operator Operator address
*/
event WorkerConfirmed(address indexed stakingProvider, address indexed worker);
event OperatorConfirmed(address indexed stakingProvider, address indexed operator);
struct StakingProviderInfo {
address worker;
bool workerConfirmed;
uint256 workerStartTimestamp;
address operator;
bool operatorConfirmed;
uint256 operatorStartTimestamp;
}
uint256 public immutable minAuthorization;
uint256 public immutable minWorkerSeconds;
uint256 public immutable minOperatorSeconds;
IStaking public immutable tStaking;
mapping (address => StakingProviderInfo) public stakingProviderInfo;
address[] public stakingProviders;
mapping(address => address) internal _stakingProviderFromWorker;
mapping(address => address) internal _stakingProviderFromOperator;
/**
* @notice Constructor sets address of token contract and parameters for staking
* @param _tStaking T token staking contract
* @param _minAuthorization Amount of minimum allowable authorization
* @param _minWorkerSeconds Min amount of seconds while a worker can't be changed
* @param _minOperatorSeconds Min amount of seconds while an operator can't be changed
*/
constructor(
IStaking _tStaking,
uint256 _minAuthorization,
uint256 _minWorkerSeconds
uint256 _minOperatorSeconds
) {
require(
_tStaking.authorizedStake(address(this), address(this)) == 0,
@ -60,7 +60,7 @@ contract SimplePREApplication {
);
minAuthorization = _minAuthorization;
tStaking = _tStaking;
minWorkerSeconds = _minWorkerSeconds;
minOperatorSeconds = _minOperatorSeconds;
}
/**
@ -79,17 +79,17 @@ contract SimplePREApplication {
//-------------------------Main-------------------------
/**
* @notice Returns staking provider for specified worker
* @notice Returns staking provider for specified operator
*/
function stakingProviderFromWorker(address _worker) public view returns (address) {
return _stakingProviderFromWorker[_worker];
function stakingProviderFromOperator(address _operator) public view returns (address) {
return _stakingProviderFromOperator[_operator];
}
/**
* @notice Returns worker for specified staking provider
* @notice Returns operator for specified staking provider
*/
function getWorkerFromStakingProvider(address _stakingProvider) public view returns (address) {
return stakingProviderInfo[_stakingProvider].worker;
function getOperatorFromStakingProvider(address _stakingProvider) public view returns (address) {
return stakingProviderInfo[_stakingProvider].operator;
}
/**
@ -126,7 +126,7 @@ contract SimplePREApplication {
address stakingProvider = stakingProviders[i];
StakingProviderInfo storage info = stakingProviderInfo[stakingProvider];
uint256 eligibleAmount = authorizedStake(stakingProvider);
if (eligibleAmount < minAuthorization || !info.workerConfirmed) {
if (eligibleAmount < minAuthorization || !info.operatorConfirmed) {
continue;
}
activeStakingProviders[resultIndex][0] = uint256(uint160(stakingProvider));
@ -153,13 +153,13 @@ contract SimplePREApplication {
}
/**
* @notice Returns true if worker has confirmed address
* @notice Returns true if operator has confirmed address
*/
// TODO maybe _stakingProvider instead of _worker?
function isWorkerConfirmed(address _worker) public view returns (bool) {
address stakingProvider = _stakingProviderFromWorker[_worker];
// TODO maybe _stakingProvider instead of _operator as input?
function isOperatorConfirmed(address _operator) public view returns (bool) {
address stakingProvider = _stakingProviderFromOperator[_operator];
StakingProviderInfo storage info = stakingProviderInfo[stakingProvider];
return info.workerConfirmed;
return info.operatorConfirmed;
}
/**
@ -170,57 +170,57 @@ contract SimplePREApplication {
}
/**
* @notice Bond worker
* @notice Bond operator
* @param _stakingProvider Staking provider address
* @param _worker Worker address. Must be a real address, not a contract
* @param _operator Operator address. Must be a real address, not a contract
*/
function bondWorker(address _stakingProvider, address _worker)
function bondOperator(address _stakingProvider, address _operator)
external onlyOwnerOrStakingProvider(_stakingProvider)
{
StakingProviderInfo storage info = stakingProviderInfo[_stakingProvider];
require(_worker != info.worker, "Specified worker is already bonded with this provider");
// If this staker had a worker ...
if (info.worker != address(0)) {
require(_operator != info.operator, "Specified operator is already bonded with this provider");
// If this staker had a operator ...
if (info.operator != address(0)) {
require(
block.timestamp >= info.workerStartTimestamp + minWorkerSeconds,
"Not enough time passed to change worker"
block.timestamp >= info.operatorStartTimestamp + minOperatorSeconds,
"Not enough time passed to change operator"
);
// Remove the old relation "worker->stakingProvider"
_stakingProviderFromWorker[info.worker] = address(0);
// Remove the old relation "operator->stakingProvider"
_stakingProviderFromOperator[info.operator] = address(0);
}
if (_worker != address(0)) {
require(_stakingProviderFromWorker[_worker] == address(0), "Specified worker is already in use");
if (_operator != address(0)) {
require(_stakingProviderFromOperator[_operator] == address(0), "Specified operator is already in use");
require(
_worker == _stakingProvider || getBeneficiary(_worker) == address(0),
"Specified worker is a provider"
_operator == _stakingProvider || getBeneficiary(_operator) == address(0),
"Specified operator is a provider"
);
// Set new worker->stakingProvider relation
_stakingProviderFromWorker[_worker] = _stakingProvider;
// Set new operator->stakingProvider relation
_stakingProviderFromOperator[_operator] = _stakingProvider;
}
if (info.workerStartTimestamp == 0) {
if (info.operatorStartTimestamp == 0) {
stakingProviders.push(_stakingProvider);
}
// Bond new worker (or unbond if _worker == address(0))
info.worker = _worker;
info.workerStartTimestamp = block.timestamp;
info.workerConfirmed = false;
emit WorkerBonded(_stakingProvider, _worker, block.timestamp);
// Bond new operator (or unbond if _operator == address(0))
info.operator = _operator;
info.operatorStartTimestamp = block.timestamp;
info.operatorConfirmed = false;
emit OperatorBonded(_stakingProvider, _operator, block.timestamp);
}
/**
* @notice Make a confirmation by worker
* @notice Make a confirmation by operator
*/
function confirmWorkerAddress() external {
address stakingProvider = _stakingProviderFromWorker[msg.sender];
require(isAuthorized(stakingProvider), "No stake associated with the worker");
function confirmOperatorAddress() external {
address stakingProvider = _stakingProviderFromOperator[msg.sender];
require(isAuthorized(stakingProvider), "No stake associated with the operator");
StakingProviderInfo storage info = stakingProviderInfo[stakingProvider];
require(!info.workerConfirmed, "Worker address is already confirmed");
require(msg.sender == tx.origin, "Only worker with real address can make a confirmation");
info.workerConfirmed = true;
emit WorkerConfirmed(stakingProvider, msg.sender);
require(!info.operatorConfirmed, "Operator address is already confirmed");
require(msg.sender == tx.origin, "Only operator with real address can make a confirmation");
info.operatorConfirmed = true;
emit OperatorConfirmed(stakingProvider, msg.sender);
}
}

View File

@ -128,7 +128,7 @@ contract ThresholdStakingForPREApplicationMock {
/**
* @notice Intermediary contract for testing worker
* @notice Intermediary contract for testing operator
*/
contract Intermediary {
@ -138,12 +138,12 @@ contract Intermediary {
preApplication = _preApplication;
}
function bondWorker(address _worker) external {
preApplication.bondWorker(address(this), _worker);
function bondOperator(address _operator) external {
preApplication.bondOperator(address(this), _operator);
}
function confirmWorkerAddress() external {
preApplication.confirmWorkerAddress();
function confirmOperatorAddress() external {
preApplication.confirmOperatorAddress();
}
}

View File

@ -24,14 +24,14 @@ from eth_utils import to_checksum_address
CONFIRMATION_SLOT = 1
def test_bond_worker(testerchain, threshold_staking, pre_application, token_economics):
def test_bond_operator(testerchain, threshold_staking, pre_application, token_economics):
creator, staking_provider_1, staking_provider_2, staking_provider_3, staking_provider_4, \
worker1, worker2, worker3, owner3, beneficiary, authorizer, *everyone_else = \
operator1, operator2, operator3, owner3, beneficiary, authorizer, *everyone_else = \
testerchain.client.accounts
min_authorization = token_economics.minimum_allowed_locked
min_worker_seconds = 24 * 60 * 60
min_operator_seconds = 24 * 60 * 60
worker_log = pre_application.events.WorkerBonded.createFilter(fromBlock='latest')
operator_log = pre_application.events.OperatorBonded.createFilter(fromBlock='latest')
# Prepare staking providers: two with intermediary contract and two just a staking provider
tx = threshold_staking.functions.setRoles(staking_provider_1).transact()
@ -52,46 +52,48 @@ def test_bond_worker(testerchain, threshold_staking, pre_application, token_econ
tx = threshold_staking.functions.setStakes(staking_provider_4, 0, 0, min_authorization).transact()
testerchain.wait_for_receipt(tx)
assert pre_application.functions.getWorkerFromStakingProvider(staking_provider_1).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromWorker(staking_provider_1).call() == NULL_ADDRESS
assert pre_application.functions.getWorkerFromStakingProvider(staking_provider_2).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromWorker(staking_provider_2).call() == NULL_ADDRESS
assert pre_application.functions.getWorkerFromStakingProvider(staking_provider_3).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromWorker(staking_provider_3).call() == NULL_ADDRESS
assert pre_application.functions.getWorkerFromStakingProvider(staking_provider_4).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromWorker(staking_provider_4).call() == NULL_ADDRESS
assert pre_application.functions.getOperatorFromStakingProvider(staking_provider_1).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromOperator(staking_provider_1).call() == NULL_ADDRESS
assert pre_application.functions.getOperatorFromStakingProvider(staking_provider_2).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromOperator(staking_provider_2).call() == NULL_ADDRESS
assert pre_application.functions.getOperatorFromStakingProvider(staking_provider_3).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromOperator(staking_provider_3).call() == NULL_ADDRESS
assert pre_application.functions.getOperatorFromStakingProvider(staking_provider_4).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromOperator(staking_provider_4).call() == NULL_ADDRESS
# Staking provider can't confirm worker address because there is no worker by default
# Staking provider can't confirm operator address because there is no operator by default
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.confirmWorkerAddress().transact({'from': staking_provider_1})
tx = pre_application.functions.confirmOperatorAddress().transact({'from': staking_provider_1})
testerchain.wait_for_receipt(tx)
# Staking provider can't bond another staking provider as worker
# Staking provider can't bond another staking provider as operator
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.bondWorker(staking_provider_1, staking_provider_2).transact({'from': staking_provider_1})
tx = pre_application.functions.bondOperator(staking_provider_1, staking_provider_2)\
.transact({'from': staking_provider_1})
testerchain.wait_for_receipt(tx)
# Staking provider can't bond worker if stake is less than minimum
# Staking provider can't bond operator if stake is less than minimum
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.bondWorker(staking_provider_2, worker1).transact({'from': staking_provider_2})
tx = pre_application.functions.bondOperator(staking_provider_2, operator1)\
.transact({'from': staking_provider_2})
testerchain.wait_for_receipt(tx)
# Only staking provider or stake owner can bond worker
# Only staking provider or stake owner can bond operator
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.bondWorker(staking_provider_3, worker1).transact({'from': beneficiary})
tx = pre_application.functions.bondOperator(staking_provider_3, operator1).transact({'from': beneficiary})
testerchain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.bondWorker(staking_provider_3, worker1).transact({'from': authorizer})
tx = pre_application.functions.bondOperator(staking_provider_3, operator1).transact({'from': authorizer})
testerchain.wait_for_receipt(tx)
# Staking provider bonds worker and now worker can make a confirmation
tx = pre_application.functions.bondWorker(staking_provider_3, worker1).transact({'from': owner3})
# Staking provider bonds operator and now operator can make a confirmation
tx = pre_application.functions.bondOperator(staking_provider_3, operator1).transact({'from': owner3})
testerchain.wait_for_receipt(tx)
timestamp = testerchain.w3.eth.getBlock('latest').timestamp
assert pre_application.functions.getWorkerFromStakingProvider(staking_provider_3).call() == worker1
assert pre_application.functions.stakingProviderFromWorker(worker1).call() == staking_provider_3
assert pre_application.functions.getOperatorFromStakingProvider(staking_provider_3).call() == operator1
assert pre_application.functions.stakingProviderFromOperator(operator1).call() == staking_provider_3
assert not pre_application.functions.stakingProviderInfo(staking_provider_3).call()[CONFIRMATION_SLOT]
assert not pre_application.functions.isWorkerConfirmed(worker1).call()
assert not pre_application.functions.isOperatorConfirmed(operator1).call()
assert pre_application.functions.getStakingProvidersLength().call() == 1
assert pre_application.functions.stakingProviders(0).call() == staking_provider_3
@ -100,205 +102,205 @@ def test_bond_worker(testerchain, threshold_staking, pre_application, token_econ
assert all_locked == 0
assert len(staking_providers) == 0
tx = pre_application.functions.confirmWorkerAddress().transact({'from': worker1})
tx = pre_application.functions.confirmOperatorAddress().transact({'from': operator1})
testerchain.wait_for_receipt(tx)
assert pre_application.functions.stakingProviderInfo(staking_provider_3).call()[CONFIRMATION_SLOT]
assert pre_application.functions.isWorkerConfirmed(worker1).call()
assert pre_application.functions.isOperatorConfirmed(operator1).call()
number_of_events = 1
events = worker_log.get_all_entries()
events = operator_log.get_all_entries()
assert len(events) == number_of_events
event_args = events[-1]['args']
assert event_args['stakingProvider'] == staking_provider_3
assert event_args['worker'] == worker1
assert event_args['operator'] == operator1
assert event_args['startTimestamp'] == timestamp
# After confirmation worker is becoming active
# After confirmation operator is becoming active
all_locked, staking_providers = pre_application.functions.getActiveStakingProviders(0, 0).call()
assert all_locked == min_authorization
assert len(staking_providers) == 1
assert to_checksum_address(staking_providers[0][0]) == staking_provider_3
assert staking_providers[0][1] == min_authorization
# Worker is in use so other stakingProviders can't bond him
# Operator is in use so other stakingProviders can't bond him
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.bondWorker(staking_provider_4, worker1).transact({'from': staking_provider_4})
tx = pre_application.functions.bondOperator(staking_provider_4, operator1).transact({'from': staking_provider_4})
testerchain.wait_for_receipt(tx)
# # Worker can't be a staking provider
# tx = threshold_staking.functions.setRoles(worker1).transact()
# # Operator can't be a staking provider
# tx = threshold_staking.functions.setRoles(operator1).transact()
# testerchain.wait_for_receipt(tx)
# tx = threshold_staking.functions.setStakes(worker1, min_authorization, 0, 0).transact()
# tx = threshold_staking.functions.setStakes(operator1, min_authorization, 0, 0).transact()
# testerchain.wait_for_receipt(tx)
# with pytest.raises((TransactionFailed, ValueError)):
# tx = threshold_staking.functions.increaseAuthorization(
# worker1, min_authorization, pre_application.address).transact({'from': worker1})
# operator1, min_authorization, pre_application.address).transact({'from': operator1})
# testerchain.wait_for_receipt(tx)
# Can't bond worker twice too soon
# Can't bond operator twice too soon
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.bondWorker(staking_provider_3, worker2).transact({'from': staking_provider_3})
tx = pre_application.functions.bondOperator(staking_provider_3, operator2).transact({'from': staking_provider_3})
testerchain.wait_for_receipt(tx)
# She can't unbond her worker too, until enough time has passed
# She can't unbond her operator too, until enough time has passed
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.bondWorker(staking_provider_3, NULL_ADDRESS).transact({'from': staking_provider_3})
tx = pre_application.functions.bondOperator(staking_provider_3, NULL_ADDRESS).transact({'from': staking_provider_3})
testerchain.wait_for_receipt(tx)
# Let's advance some time and unbond the worker
testerchain.time_travel(seconds=min_worker_seconds)
tx = pre_application.functions.bondWorker(staking_provider_3, NULL_ADDRESS).transact({'from': staking_provider_3})
# Let's advance some time and unbond the operator
testerchain.time_travel(seconds=min_operator_seconds)
tx = pre_application.functions.bondOperator(staking_provider_3, NULL_ADDRESS).transact({'from': staking_provider_3})
testerchain.wait_for_receipt(tx)
timestamp = testerchain.w3.eth.getBlock('latest').timestamp
assert pre_application.functions.getWorkerFromStakingProvider(staking_provider_3).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromWorker(staking_provider_3).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromWorker(worker1).call() == NULL_ADDRESS
assert pre_application.functions.getOperatorFromStakingProvider(staking_provider_3).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromOperator(staking_provider_3).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromOperator(operator1).call() == NULL_ADDRESS
assert not pre_application.functions.stakingProviderInfo(staking_provider_3).call()[CONFIRMATION_SLOT]
assert not pre_application.functions.isWorkerConfirmed(worker1).call()
assert not pre_application.functions.isOperatorConfirmed(operator1).call()
assert pre_application.functions.getStakingProvidersLength().call() == 1
assert pre_application.functions.stakingProviders(0).call() == staking_provider_3
# Resetting worker removes from active list before next confirmation
# Resetting operator removes from active list before next confirmation
all_locked, staking_providers = pre_application.functions.getActiveStakingProviders(0, 0).call()
assert all_locked == 0
assert len(staking_providers) == 0
number_of_events += 1
events = worker_log.get_all_entries()
events = operator_log.get_all_entries()
assert len(events) == number_of_events
event_args = events[-1]['args']
assert event_args['stakingProvider'] == staking_provider_3
# Now the worker has been unbonded ...
assert event_args['worker'] == NULL_ADDRESS
# Now the operator has been unbonded ...
assert event_args['operator'] == NULL_ADDRESS
# ... with a new starting period.
assert event_args['startTimestamp'] == timestamp
# The staking provider can bond now a new worker, without waiting additional time.
tx = pre_application.functions.bondWorker(staking_provider_3, worker2).transact({'from': staking_provider_3})
# The staking provider can bond now a new operator, without waiting additional time.
tx = pre_application.functions.bondOperator(staking_provider_3, operator2).transact({'from': staking_provider_3})
testerchain.wait_for_receipt(tx)
timestamp = testerchain.w3.eth.getBlock('latest').timestamp
assert pre_application.functions.getWorkerFromStakingProvider(staking_provider_3).call() == worker2
assert pre_application.functions.stakingProviderFromWorker(worker2).call() == staking_provider_3
assert pre_application.functions.getOperatorFromStakingProvider(staking_provider_3).call() == operator2
assert pre_application.functions.stakingProviderFromOperator(operator2).call() == staking_provider_3
assert not pre_application.functions.stakingProviderInfo(staking_provider_3).call()[CONFIRMATION_SLOT]
assert not pre_application.functions.isWorkerConfirmed(worker2).call()
assert not pre_application.functions.isOperatorConfirmed(operator2).call()
assert pre_application.functions.getStakingProvidersLength().call() == 1
assert pre_application.functions.stakingProviders(0).call() == staking_provider_3
number_of_events += 1
events = worker_log.get_all_entries()
events = operator_log.get_all_entries()
assert len(events) == number_of_events
event_args = events[-1]['args']
assert event_args['stakingProvider'] == staking_provider_3
assert event_args['worker'] == worker2
assert event_args['operator'] == operator2
assert event_args['startTimestamp'] == timestamp
# Now the previous worker can no longer make a confirmation
# Now the previous operator can no longer make a confirmation
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.confirmWorkerAddress().transact({'from': worker1})
tx = pre_application.functions.confirmOperatorAddress().transact({'from': operator1})
testerchain.wait_for_receipt(tx)
# Only new worker can
tx = pre_application.functions.confirmWorkerAddress().transact({'from': worker2})
# Only new operator can
tx = pre_application.functions.confirmOperatorAddress().transact({'from': operator2})
testerchain.wait_for_receipt(tx)
assert not pre_application.functions.isWorkerConfirmed(worker1).call()
assert pre_application.functions.isWorkerConfirmed(worker2).call()
assert not pre_application.functions.isOperatorConfirmed(operator1).call()
assert pre_application.functions.isOperatorConfirmed(operator2).call()
assert pre_application.functions.stakingProviderInfo(staking_provider_3).call()[CONFIRMATION_SLOT]
# Another staker can bond a free worker
tx = pre_application.functions.bondWorker(staking_provider_4, worker1).transact({'from': staking_provider_4})
# Another staker can bond a free operator
tx = pre_application.functions.bondOperator(staking_provider_4, operator1).transact({'from': staking_provider_4})
testerchain.wait_for_receipt(tx)
timestamp = testerchain.w3.eth.getBlock('latest').timestamp
assert pre_application.functions.getWorkerFromStakingProvider(staking_provider_4).call() == worker1
assert pre_application.functions.stakingProviderFromWorker(worker1).call() == staking_provider_4
assert not pre_application.functions.isWorkerConfirmed(worker1).call()
assert pre_application.functions.getOperatorFromStakingProvider(staking_provider_4).call() == operator1
assert pre_application.functions.stakingProviderFromOperator(operator1).call() == staking_provider_4
assert not pre_application.functions.isOperatorConfirmed(operator1).call()
assert not pre_application.functions.stakingProviderInfo(staking_provider_4).call()[CONFIRMATION_SLOT]
assert pre_application.functions.getStakingProvidersLength().call() == 2
assert pre_application.functions.stakingProviders(1).call() == staking_provider_4
number_of_events += 1
events = worker_log.get_all_entries()
events = operator_log.get_all_entries()
assert len(events) == number_of_events
event_args = events[-1]['args']
assert event_args['stakingProvider'] == staking_provider_4
assert event_args['worker'] == worker1
assert event_args['operator'] == operator1
assert event_args['startTimestamp'] == timestamp
# # The first worker still can't be a staking provider
# tx = threshold_staking.functions.setRoles(worker1).transact()
# # The first operator still can't be a staking provider
# tx = threshold_staking.functions.setRoles(operator1).transact()
# testerchain.wait_for_receipt(tx)
# tx = threshold_staking.functions.setStakes(worker1, min_authorization, 0, 0).transact()
# tx = threshold_staking.functions.setStakes(operator1, min_authorization, 0, 0).transact()
# testerchain.wait_for_receipt(tx)
# with pytest.raises((TransactionFailed, ValueError)):
# tx = threshold_staking.functions.increaseAuthorization(
# worker1, min_authorization, pre_application.address).transact({'from': worker1})
# operator1, min_authorization, pre_application.address).transact({'from': operator1})
# testerchain.wait_for_receipt(tx)
# Bond worker again
tx = pre_application.functions.confirmWorkerAddress().transact({'from': worker1})
# Bond operator again
tx = pre_application.functions.confirmOperatorAddress().transact({'from': operator1})
testerchain.wait_for_receipt(tx)
assert pre_application.functions.isWorkerConfirmed(worker1).call()
assert pre_application.functions.isOperatorConfirmed(operator1).call()
assert pre_application.functions.stakingProviderInfo(staking_provider_4).call()[CONFIRMATION_SLOT]
testerchain.time_travel(seconds=min_worker_seconds)
tx = pre_application.functions.bondWorker(staking_provider_4, worker3).transact({'from': staking_provider_4})
testerchain.time_travel(seconds=min_operator_seconds)
tx = pre_application.functions.bondOperator(staking_provider_4, operator3).transact({'from': staking_provider_4})
testerchain.wait_for_receipt(tx)
timestamp = testerchain.w3.eth.getBlock('latest').timestamp
assert pre_application.functions.getWorkerFromStakingProvider(staking_provider_4).call() == worker3
assert pre_application.functions.stakingProviderFromWorker(worker3).call() == staking_provider_4
assert pre_application.functions.stakingProviderFromWorker(worker1).call() == NULL_ADDRESS
assert not pre_application.functions.isWorkerConfirmed(worker3).call()
assert not pre_application.functions.isWorkerConfirmed(worker1).call()
assert pre_application.functions.getOperatorFromStakingProvider(staking_provider_4).call() == operator3
assert pre_application.functions.stakingProviderFromOperator(operator3).call() == staking_provider_4
assert pre_application.functions.stakingProviderFromOperator(operator1).call() == NULL_ADDRESS
assert not pre_application.functions.isOperatorConfirmed(operator3).call()
assert not pre_application.functions.isOperatorConfirmed(operator1).call()
assert not pre_application.functions.stakingProviderInfo(staking_provider_4).call()[CONFIRMATION_SLOT]
assert pre_application.functions.getStakingProvidersLength().call() == 2
assert pre_application.functions.stakingProviders(1).call() == staking_provider_4
# Resetting worker removes from active list before next confirmation
# Resetting operator removes from active list before next confirmation
all_locked, staking_providers = pre_application.functions.getActiveStakingProviders(1, 0).call()
assert all_locked == 0
assert len(staking_providers) == 0
number_of_events += 1
events = worker_log.get_all_entries()
events = operator_log.get_all_entries()
assert len(events) == number_of_events
event_args = events[-1]['args']
assert event_args['stakingProvider'] == staking_provider_4
assert event_args['worker'] == worker3
assert event_args['operator'] == operator3
assert event_args['startTimestamp'] == timestamp
# The first worker is free and can deposit tokens and become a staker
tx = threshold_staking.functions.setRoles(worker1).transact()
# The first operator is free and can deposit tokens and become a staker
tx = threshold_staking.functions.setRoles(operator1).transact()
testerchain.wait_for_receipt(tx)
tx = threshold_staking.functions.setStakes(
worker1, min_authorization // 3, min_authorization // 3, min_authorization // 3).transact()
operator1, min_authorization // 3, min_authorization // 3, min_authorization // 3).transact()
testerchain.wait_for_receipt(tx)
# tx = threshold_staking.functions.increaseAuthorization(
# worker1, min_authorization, pre_application.address).transact({'from': worker1})
# operator1, min_authorization, pre_application.address).transact({'from': operator1})
# testerchain.wait_for_receipt(tx)
assert pre_application.functions.getWorkerFromStakingProvider(worker1).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromWorker(worker1).call() == NULL_ADDRESS
assert pre_application.functions.getOperatorFromStakingProvider(operator1).call() == NULL_ADDRESS
assert pre_application.functions.stakingProviderFromOperator(operator1).call() == NULL_ADDRESS
testerchain.time_travel(seconds=min_worker_seconds)
testerchain.time_travel(seconds=min_operator_seconds)
# Staking provider can't bond the first worker again because worker is a provider now
# Staking provider can't bond the first operator again because operator is a provider now
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.bondWorker(staking_provider_4, worker1).transact({'from': staking_provider_4})
tx = pre_application.functions.bondOperator(staking_provider_4, operator1).transact({'from': staking_provider_4})
testerchain.wait_for_receipt(tx)
# Provider without intermediary contract can bond itself as worker
# Provider without intermediary contract can bond itself as operator
# (Probably not best idea, but whatever)
tx = pre_application.functions.bondWorker(
tx = pre_application.functions.bondOperator(
staking_provider_1, staking_provider_1).transact({'from': staking_provider_1})
testerchain.wait_for_receipt(tx)
timestamp = testerchain.w3.eth.getBlock('latest').timestamp
assert pre_application.functions.getWorkerFromStakingProvider(staking_provider_1).call() == staking_provider_1
assert pre_application.functions.stakingProviderFromWorker(staking_provider_1).call() == staking_provider_1
assert pre_application.functions.getOperatorFromStakingProvider(staking_provider_1).call() == staking_provider_1
assert pre_application.functions.stakingProviderFromOperator(staking_provider_1).call() == staking_provider_1
assert pre_application.functions.getStakingProvidersLength().call() == 3
assert pre_application.functions.stakingProviders(2).call() == staking_provider_1
number_of_events += 1
events = worker_log.get_all_entries()
events = operator_log.get_all_entries()
assert len(events) == number_of_events
event_args = events[-1]['args']
assert event_args['stakingProvider'] == staking_provider_1
assert event_args['worker'] == staking_provider_1
assert event_args['operator'] == staking_provider_1
assert event_args['startTimestamp'] == timestamp
# If stake will be less than minimum then confirmation is not possible
@ -306,13 +308,13 @@ def test_bond_worker(testerchain, threshold_staking, pre_application, token_econ
testerchain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.confirmWorkerAddress().transact({'from': staking_provider_1})
tx = pre_application.functions.confirmOperatorAddress().transact({'from': staking_provider_1})
testerchain.wait_for_receipt(tx)
# Now provider can make a confirmation
tx = threshold_staking.functions.setStakes(staking_provider_1, 0, 0, min_authorization).transact()
testerchain.wait_for_receipt(tx)
tx = pre_application.functions.confirmWorkerAddress().transact({'from': staking_provider_1})
tx = pre_application.functions.confirmOperatorAddress().transact({'from': staking_provider_1})
testerchain.wait_for_receipt(tx)
# If stake will be less than minimum then provider is not active
@ -331,54 +333,54 @@ def test_bond_worker(testerchain, threshold_staking, pre_application, token_econ
def test_confirm_address(testerchain, threshold_staking, pre_application, token_economics, deploy_contract):
creator, staking_provider, worker, *everyone_else = testerchain.client.accounts
creator, staking_provider, operator, *everyone_else = testerchain.client.accounts
min_authorization = token_economics.minimum_allowed_locked
min_worker_seconds = 24 * 60 * 60
min_operator_seconds = 24 * 60 * 60
confirmations_log = pre_application.events.WorkerConfirmed.createFilter(fromBlock='latest')
confirmations_log = pre_application.events.OperatorConfirmed.createFilter(fromBlock='latest')
# Worker must be associated with provider that has minimum amount of tokens
# Operator must be associated with provider that has minimum amount of tokens
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.confirmWorkerAddress().transact({'from': staking_provider})
tx = pre_application.functions.confirmOperatorAddress().transact({'from': staking_provider})
testerchain.wait_for_receipt(tx)
tx = threshold_staking.functions.setRoles(staking_provider).transact()
testerchain.wait_for_receipt(tx)
tx = threshold_staking.functions.setStakes(staking_provider, min_authorization - 1, 0, 0).transact()
testerchain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.confirmWorkerAddress().transact({'from': staking_provider})
tx = pre_application.functions.confirmOperatorAddress().transact({'from': staking_provider})
testerchain.wait_for_receipt(tx)
# Deploy intermediary contract
intermediary, _ = deploy_contract('Intermediary', pre_application.address)
# Bond contract as a worker
# Bond contract as a operator
tx = threshold_staking.functions.setStakes(staking_provider, min_authorization, 0, 0).transact()
testerchain.wait_for_receipt(tx)
tx = pre_application.functions.bondWorker(staking_provider, intermediary.address).transact({'from': staking_provider})
tx = pre_application.functions.bondOperator(staking_provider, intermediary.address).transact({'from': staking_provider})
testerchain.wait_for_receipt(tx)
# But can't make a confirmation using an intermediary contract
with pytest.raises((TransactionFailed, ValueError)):
tx = intermediary.functions.confirmWorkerAddress().transact({'from': staking_provider})
tx = intermediary.functions.confirmOperatorAddress().transact({'from': staking_provider})
testerchain.wait_for_receipt(tx)
# Bond worker again and make confirmation
testerchain.time_travel(seconds=min_worker_seconds)
tx = pre_application.functions.bondWorker(staking_provider, worker).transact({'from': staking_provider})
# Bond operator again and make confirmation
testerchain.time_travel(seconds=min_operator_seconds)
tx = pre_application.functions.bondOperator(staking_provider, operator).transact({'from': staking_provider})
testerchain.wait_for_receipt(tx)
tx = pre_application.functions.confirmWorkerAddress().transact({'from': worker})
tx = pre_application.functions.confirmOperatorAddress().transact({'from': operator})
testerchain.wait_for_receipt(tx)
assert pre_application.functions.isWorkerConfirmed(worker).call()
assert pre_application.functions.isOperatorConfirmed(operator).call()
assert pre_application.functions.stakingProviderInfo(staking_provider).call()[CONFIRMATION_SLOT]
events = confirmations_log.get_all_entries()
assert len(events) == 1
event_args = events[-1]['args']
assert event_args['stakingProvider'] == staking_provider
assert event_args['worker'] == worker
assert event_args['operator'] == operator
# Can't confirm twice
with pytest.raises((TransactionFailed, ValueError)):
tx = pre_application.functions.confirmWorkerAddress().transact({'from': worker})
tx = pre_application.functions.confirmOperatorAddress().transact({'from': operator})
testerchain.wait_for_receipt(tx)