mirror of https://github.com/nucypher/nucypher.git
WorkLock: small refactoring of methods and tests
parent
8571f220f4
commit
41cbd542ee
|
@ -1734,8 +1734,8 @@ class Bidder(NucypherTokenActor):
|
||||||
receipt = self.worklock_agent.cancel_bid(checksum_address=self.checksum_address)
|
receipt = self.worklock_agent.cancel_bid(checksum_address=self.checksum_address)
|
||||||
return receipt
|
return receipt
|
||||||
|
|
||||||
def _get_maximum_allowed_bid(self) -> int:
|
def _get_max_bid_from_max_stake(self) -> int:
|
||||||
"""Returns maximum allowed bid for current deposit rate"""
|
"""Returns maximum allowed bid calculated from maximum allowed locked tokens"""
|
||||||
max_tokens = self.economics.maximum_allowed_locked
|
max_tokens = self.economics.maximum_allowed_locked
|
||||||
eth_supply = self.worklock_agent.get_eth_supply()
|
eth_supply = self.worklock_agent.get_eth_supply()
|
||||||
worklock_supply = self.economics.worklock_supply
|
worklock_supply = self.economics.worklock_supply
|
||||||
|
@ -1745,20 +1745,24 @@ class Bidder(NucypherTokenActor):
|
||||||
# TODO make public and CLI command to print the list
|
# TODO make public and CLI command to print the list
|
||||||
def _get_incorrect_bids(self) -> List[str]:
|
def _get_incorrect_bids(self) -> List[str]:
|
||||||
bidders = self.worklock_agent.get_bidders()
|
bidders = self.worklock_agent.get_bidders()
|
||||||
max_bid = self._get_maximum_allowed_bid()
|
max_bid_from_max_stake = self._get_max_bid_from_max_stake()
|
||||||
|
|
||||||
incorrect = list()
|
incorrect = list()
|
||||||
|
if max_bid_from_max_stake >= self.economics.worklock_max_allowed_bid:
|
||||||
|
return incorrect
|
||||||
|
|
||||||
for bidder in bidders:
|
for bidder in bidders:
|
||||||
if self.worklock_agent.get_deposited_eth(bidder) > max_bid:
|
if self.worklock_agent.get_deposited_eth(bidder) > max_bid_from_max_stake:
|
||||||
incorrect.append(bidder)
|
incorrect.append(bidder)
|
||||||
return incorrect
|
return incorrect
|
||||||
|
|
||||||
# TODO better control: max iterations, gas limit for each iteration
|
# TODO better control: max iterations, interactive mode
|
||||||
def verify_bidding_correctness(self, gas_limit: int) -> dict:
|
def verify_bidding_correctness(self, gas_limit: int) -> dict:
|
||||||
end = self.worklock_agent.end_cancellation_date
|
end = self.worklock_agent.end_cancellation_date
|
||||||
error = f"Checking of bidding is allowed only when the cancellation window is closed (closes at {end})."
|
error = f"Checking of bidding is allowed only when the cancellation window is closed (closes at {end})."
|
||||||
self._ensure_cancellation_window(ensure_closed=True, message=error)
|
self._ensure_cancellation_window(ensure_closed=True, message=error)
|
||||||
|
|
||||||
if self.worklock_agent.is_claiming_available():
|
if self.worklock_agent.bidders_checked():
|
||||||
raise self.BidderError(f"Check has already done")
|
raise self.BidderError(f"Check has already done")
|
||||||
|
|
||||||
incorrect_bidders = self._get_incorrect_bids()
|
incorrect_bidders = self._get_incorrect_bids()
|
||||||
|
@ -1767,7 +1771,7 @@ class Bidder(NucypherTokenActor):
|
||||||
|
|
||||||
receipts = dict()
|
receipts = dict()
|
||||||
iteration = 1
|
iteration = 1
|
||||||
while not self.worklock_agent.is_claiming_available():
|
while not self.worklock_agent.bidders_checked():
|
||||||
receipt = self.worklock_agent.verify_bidding_correctness(checksum_address=self.checksum_address,
|
receipt = self.worklock_agent.verify_bidding_correctness(checksum_address=self.checksum_address,
|
||||||
gas_limit=gas_limit)
|
gas_limit=gas_limit)
|
||||||
receipts[iteration] = receipt
|
receipts[iteration] = receipt
|
||||||
|
|
|
@ -1139,9 +1139,14 @@ class WorkLockAgent(EthereumContractAgent):
|
||||||
return bidders
|
return bidders
|
||||||
|
|
||||||
def is_claiming_available(self) -> bool:
|
def is_claiming_available(self) -> bool:
|
||||||
"""Returns True if bidders have been checked and claiming is available"""
|
"""Returns True if claiming is available"""
|
||||||
return self.contract.functions.isClaimingAvailable().call()
|
return self.contract.functions.isClaimingAvailable().call()
|
||||||
|
|
||||||
|
def bidders_checked(self) -> bool:
|
||||||
|
"""Returns True if bidders have been checked"""
|
||||||
|
bidders_population = self.get_bidders_population()
|
||||||
|
return self.contract.functions.nextBidderToCheck().call() == bidders_population
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def minimum_allowed_bid(self) -> int:
|
def minimum_allowed_bid(self) -> int:
|
||||||
min_bid = self.contract.functions.minAllowedBid().call()
|
min_bid = self.contract.functions.minAllowedBid().call()
|
||||||
|
|
|
@ -25,6 +25,7 @@ contract WorkLock {
|
||||||
event Refund(address indexed sender, uint256 refundETH, uint256 completedWork);
|
event Refund(address indexed sender, uint256 refundETH, uint256 completedWork);
|
||||||
event Canceled(address indexed sender, uint256 value);
|
event Canceled(address indexed sender, uint256 value);
|
||||||
event BiddersChecked(address indexed sender, uint256 startIndex, uint256 endIndex);
|
event BiddersChecked(address indexed sender, uint256 startIndex, uint256 endIndex);
|
||||||
|
event ClaimingEnabled(address indexed sender);
|
||||||
|
|
||||||
struct WorkInfo {
|
struct WorkInfo {
|
||||||
uint256 depositedETH;
|
uint256 depositedETH;
|
||||||
|
@ -221,12 +222,12 @@ contract WorkLock {
|
||||||
"Checking bidders is allowed when bidding and cancellation phases are over");
|
"Checking bidders is allowed when bidding and cancellation phases are over");
|
||||||
require(nextBidderToCheck != bidders.length, "Bidders have already been checked");
|
require(nextBidderToCheck != bidders.length, "Bidders have already been checked");
|
||||||
|
|
||||||
uint256 maxAllowableBid = maxAllowableLockedTokens.mul(ethSupply).div(tokenSupply);
|
uint256 maxBidFromMaxStake = maxAllowableLockedTokens.mul(ethSupply).div(tokenSupply);
|
||||||
uint256 index = nextBidderToCheck;
|
uint256 index = nextBidderToCheck;
|
||||||
|
|
||||||
while (index < bidders.length && gasleft() > _gasToSaveState) {
|
while (index < bidders.length && gasleft() > _gasToSaveState) {
|
||||||
address bidder = bidders[index];
|
address bidder = bidders[index];
|
||||||
require(workInfo[bidder].depositedETH <= maxAllowableBid);
|
require(workInfo[bidder].depositedETH <= maxBidFromMaxStake);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +252,7 @@ contract WorkLock {
|
||||||
function claim() external returns (uint256 claimedTokens) {
|
function claim() external returns (uint256 claimedTokens) {
|
||||||
require(block.timestamp >= endCancellationDate,
|
require(block.timestamp >= endCancellationDate,
|
||||||
"Claiming tokens is allowed when bidding and cancellation phases are over");
|
"Claiming tokens is allowed when bidding and cancellation phases are over");
|
||||||
require(nextBidderToCheck == bidders.length, "Bidders have not been checked");
|
require(isClaimingAvailable(), "Claiming has not been enabled yet");
|
||||||
WorkInfo storage info = workInfo[msg.sender];
|
WorkInfo storage info = workInfo[msg.sender];
|
||||||
require(!info.claimed, "Tokens are already claimed");
|
require(!info.claimed, "Tokens are already claimed");
|
||||||
claimedTokens = ethToTokens(info.depositedETH);
|
claimedTokens = ethToTokens(info.depositedETH);
|
||||||
|
|
|
@ -271,9 +271,11 @@ def refund(general_config, worklock_options, registry_options, force, hw_wallet)
|
||||||
@group_worklock_options
|
@group_worklock_options
|
||||||
@option_force
|
@option_force
|
||||||
@option_hw_wallet
|
@option_hw_wallet
|
||||||
@click.option('--gas-limit', help="Gas limit per transaction", type=click.IntRange(min=1))
|
@click.option('--gas-limit', help="Gas limit per each verification transaction", type=click.IntRange(min=60000))
|
||||||
def verify_correctness(general_config, registry_options, worklock_options, force, hw_wallet, gas_limit):
|
# TODO: Consider moving to administrator (nucypher-deploy)
|
||||||
"""Verify correctness of bidding"""
|
# TODO: interactive mode for each step, choosing only specified steps
|
||||||
|
def post_initialization(general_config, registry_options, worklock_options, force, hw_wallet, gas_limit):
|
||||||
|
"""Ensure correctness of bidding and enable claiming"""
|
||||||
emitter = _setup_emitter(general_config)
|
emitter = _setup_emitter(general_config)
|
||||||
if not worklock_options.bidder_address: # TODO: Consider bundle this in worklock_options
|
if not worklock_options.bidder_address: # TODO: Consider bundle this in worklock_options
|
||||||
worklock_options.bidder_address = select_client_account(emitter=emitter,
|
worklock_options.bidder_address = select_client_account(emitter=emitter,
|
||||||
|
@ -285,15 +287,16 @@ def verify_correctness(general_config, registry_options, worklock_options, force
|
||||||
|
|
||||||
if not gas_limit:
|
if not gas_limit:
|
||||||
# TODO print gas estimations
|
# TODO print gas estimations
|
||||||
gas_limit = click.prompt(f"Enter gas limit per each transaction", type=click.IntRange(min=1))
|
gas_limit = click.prompt(f"Enter gas limit per each verification transaction", type=click.IntRange(min=60000))
|
||||||
|
|
||||||
if not force:
|
if not force:
|
||||||
click.confirm(f"Confirm verifying of bidding from {worklock_options.bidder_address} "
|
click.confirm(f"Confirm verifying of bidding from {worklock_options.bidder_address} "
|
||||||
f"using {gas_limit} gas per each transaction?", abort=True)
|
f"using {gas_limit} gas per each transaction?", abort=True)
|
||||||
|
|
||||||
receipts = bidder.verify_bidding_correctness(gas_limit=gas_limit)
|
verification_receipts = bidder.verify_bidding_correctness(gas_limit=gas_limit)
|
||||||
emitter.echo("Bidding has been checked\n", color='green')
|
emitter.echo("Bidding has been checked\n", color='green')
|
||||||
for iteration, receipt in receipts.items():
|
|
||||||
|
for iteration, receipt in verification_receipts.items():
|
||||||
paint_receipt_summary(receipt=receipt,
|
paint_receipt_summary(receipt=receipt,
|
||||||
emitter=emitter,
|
emitter=emitter,
|
||||||
chain_name=bidder.staking_agent.blockchain.client.chain_name,
|
chain_name=bidder.staking_agent.blockchain.client.chain_name,
|
||||||
|
|
|
@ -37,22 +37,22 @@ contract StakingEscrowForWorkLockMock {
|
||||||
minLockedPeriods = _minLockedPeriods;
|
minLockedPeriods = _minLockedPeriods;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCompletedWork(address _staker) public view returns (uint256) {
|
function getCompletedWork(address _staker) external view returns (uint256) {
|
||||||
return stakerInfo[_staker].completedWork;
|
return stakerInfo[_staker].completedWork;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setWorkMeasurement(address _staker, bool _measureWork) public returns (uint256) {
|
function setWorkMeasurement(address _staker, bool _measureWork) external returns (uint256) {
|
||||||
stakerInfo[_staker].measureWork = _measureWork;
|
stakerInfo[_staker].measureWork = _measureWork;
|
||||||
return stakerInfo[_staker].completedWork;
|
return stakerInfo[_staker].completedWork;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deposit(address _staker, uint256 _value, uint16 _periods) public {
|
function deposit(address _staker, uint256 _value, uint16 _periods) external {
|
||||||
stakerInfo[_staker].value = _value;
|
stakerInfo[_staker].value = _value;
|
||||||
stakerInfo[_staker].periods = _periods;
|
stakerInfo[_staker].periods = _periods;
|
||||||
token.transferFrom(msg.sender, address(this), _value);
|
token.transferFrom(msg.sender, address(this), _value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCompletedWork(address _staker, uint256 _completedWork) public {
|
function setCompletedWork(address _staker, uint256 _completedWork) external {
|
||||||
stakerInfo[_staker].completedWork = _completedWork;
|
stakerInfo[_staker].completedWork = _completedWork;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -401,6 +401,7 @@ def test_all(testerchain,
|
||||||
|
|
||||||
# Check all bidders
|
# Check all bidders
|
||||||
assert worklock.functions.getBiddersLength().call() == 2
|
assert worklock.functions.getBiddersLength().call() == 2
|
||||||
|
assert worklock.functions.nextBidderToCheck().call() == 0
|
||||||
tx = worklock.functions.verifyBiddingCorrectness(30000).transact()
|
tx = worklock.functions.verifyBiddingCorrectness(30000).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
assert worklock.functions.nextBidderToCheck().call() == 2
|
assert worklock.functions.nextBidderToCheck().call() == 2
|
||||||
|
@ -432,7 +433,6 @@ def test_all(testerchain,
|
||||||
assert escrow.functions.stakerInfo(staker2).call()[WIND_DOWN_FIELD]
|
assert escrow.functions.stakerInfo(staker2).call()[WIND_DOWN_FIELD]
|
||||||
|
|
||||||
staker1_tokens = worklock_supply // 10
|
staker1_tokens = worklock_supply // 10
|
||||||
assert escrow.functions.minAllowableLockedTokens().call() == token_economics.minimum_allowed_locked
|
|
||||||
tx = worklock.functions.claim().transact({'from': staker1, 'gas_price': 0})
|
tx = worklock.functions.claim().transact({'from': staker1, 'gas_price': 0})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
assert worklock.functions.workInfo(staker1).call()[2]
|
assert worklock.functions.workInfo(staker1).call()[2]
|
||||||
|
|
|
@ -79,8 +79,8 @@ def test_worklock(testerchain, token_economics, deploy_contract, token, escrow):
|
||||||
checks_log = worklock.events.BiddersChecked.createFilter(fromBlock='latest')
|
checks_log = worklock.events.BiddersChecked.createFilter(fromBlock='latest')
|
||||||
|
|
||||||
# Transfer tokens to WorkLock
|
# Transfer tokens to WorkLock
|
||||||
worklock_supply_1 = 2 * token_economics.maximum_allowed_locked // 10 + 1
|
worklock_supply_1 = token_economics.maximum_allowed_locked // 2 + 1
|
||||||
worklock_supply_2 = token_economics.maximum_allowed_locked // 10 - 1
|
worklock_supply_2 = token_economics.maximum_allowed_locked // 2 - 1
|
||||||
worklock_supply = worklock_supply_1 + worklock_supply_2
|
worklock_supply = worklock_supply_1 + worklock_supply_2
|
||||||
tx = token.functions.approve(worklock.address, worklock_supply).transact({'from': creator})
|
tx = token.functions.approve(worklock.address, worklock_supply).transact({'from': creator})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
@ -103,7 +103,7 @@ def test_worklock(testerchain, token_economics, deploy_contract, token, escrow):
|
||||||
# Give stakers some ETH
|
# Give stakers some ETH
|
||||||
deposit_eth_1 = 4 * min_allowed_bid
|
deposit_eth_1 = 4 * min_allowed_bid
|
||||||
deposit_eth_2 = min_allowed_bid
|
deposit_eth_2 = min_allowed_bid
|
||||||
staker1_balance = 10 * deposit_eth_1
|
staker1_balance = 100 * deposit_eth_1
|
||||||
tx = testerchain.w3.eth.sendTransaction(
|
tx = testerchain.w3.eth.sendTransaction(
|
||||||
{'from': testerchain.etherbase_account, 'to': staker1, 'value': staker1_balance})
|
{'from': testerchain.etherbase_account, 'to': staker1, 'value': staker1_balance})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
@ -333,6 +333,10 @@ def test_worklock(testerchain, token_economics, deploy_contract, token, escrow):
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = worklock.functions.claim().transact({'from': staker1, 'gas_price': 0})
|
tx = worklock.functions.claim().transact({'from': staker1, 'gas_price': 0})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
# Can't check before end of cancellation window
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact()
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
# But can cancel during cancellation window
|
# But can cancel during cancellation window
|
||||||
staker3_balance = testerchain.w3.eth.getBalance(staker3)
|
staker3_balance = testerchain.w3.eth.getBalance(staker3)
|
||||||
|
@ -357,11 +361,6 @@ def test_worklock(testerchain, token_economics, deploy_contract, token, escrow):
|
||||||
assert event_args['sender'] == staker3
|
assert event_args['sender'] == staker3
|
||||||
assert event_args['value'] == staker3_bid
|
assert event_args['value'] == staker3_bid
|
||||||
|
|
||||||
# Can't check before end of cancellation window
|
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
|
||||||
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact()
|
|
||||||
testerchain.wait_for_receipt(tx)
|
|
||||||
|
|
||||||
# Wait for the end of cancellation window
|
# Wait for the end of cancellation window
|
||||||
testerchain.time_travel(seconds=3600) # Wait exactly 1 hour
|
testerchain.time_travel(seconds=3600) # Wait exactly 1 hour
|
||||||
|
|
||||||
|
@ -370,48 +369,19 @@ def test_worklock(testerchain, token_economics, deploy_contract, token, escrow):
|
||||||
tx = worklock.functions.claim().transact({'from': staker1, 'gas_price': 0})
|
tx = worklock.functions.claim().transact({'from': staker1, 'gas_price': 0})
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
# Too low value for remaining gas
|
# Check all bidders
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
# TODO failed cases with force refund
|
||||||
tx = worklock.functions.verifyBiddingCorrectness(0).transact({'from': staker1, 'gas': 35000})
|
|
||||||
testerchain.wait_for_receipt(tx)
|
|
||||||
|
|
||||||
# Too low value for gas limit
|
|
||||||
assert worklock.functions.nextBidderToCheck().call() == 0
|
|
||||||
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact({'gas': gas_to_save_state + 5000})
|
|
||||||
testerchain.wait_for_receipt(tx)
|
|
||||||
assert worklock.functions.nextBidderToCheck().call() == 0
|
|
||||||
|
|
||||||
# Set gas only for one check
|
|
||||||
# TODO failed cases
|
|
||||||
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state)\
|
|
||||||
.transact({'from': staker1, 'gas': 60000, 'gas_price': 0})
|
|
||||||
testerchain.wait_for_receipt(tx)
|
|
||||||
assert worklock.functions.nextBidderToCheck().call() == 1
|
|
||||||
|
|
||||||
events = checks_log.get_all_entries()
|
|
||||||
assert 1 == len(events)
|
|
||||||
event_args = events[0]['args']
|
|
||||||
assert event_args['sender'] == staker1
|
|
||||||
assert event_args['startIndex'] == 0
|
|
||||||
assert event_args['endIndex'] == 1
|
|
||||||
|
|
||||||
# Still can't claim because checked only portion of bidders
|
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
|
||||||
tx = worklock.functions.claim().transact({'from': staker1, 'gas_price': 0})
|
|
||||||
testerchain.wait_for_receipt(tx)
|
|
||||||
|
|
||||||
# Check all others
|
|
||||||
assert not worklock.functions.isClaimingAvailable().call()
|
assert not worklock.functions.isClaimingAvailable().call()
|
||||||
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact()
|
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
assert worklock.functions.nextBidderToCheck().call() == 3
|
assert worklock.functions.nextBidderToCheck().call() == 3
|
||||||
assert worklock.functions.isClaimingAvailable().call()
|
assert not worklock.functions.isClaimingAvailable().call()
|
||||||
|
|
||||||
events = checks_log.get_all_entries()
|
events = checks_log.get_all_entries()
|
||||||
assert 2 == len(events)
|
assert 1 == len(events)
|
||||||
event_args = events[1]['args']
|
event_args = events[-1]['args']
|
||||||
assert event_args['sender'] == creator
|
assert event_args['sender'] == creator
|
||||||
assert event_args['startIndex'] == 1
|
assert event_args['startIndex'] == 0
|
||||||
assert event_args['endIndex'] == 3
|
assert event_args['endIndex'] == 3
|
||||||
|
|
||||||
# Can't check again
|
# Can't check again
|
||||||
|
@ -419,6 +389,13 @@ def test_worklock(testerchain, token_economics, deploy_contract, token, escrow):
|
||||||
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact()
|
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact()
|
||||||
|
assert worklock.functions.isClaimingAvailable().call()
|
||||||
|
|
||||||
|
assert 2 == len(events)
|
||||||
|
|
||||||
|
# Can't check again
|
||||||
|
|
||||||
# Staker claims tokens
|
# Staker claims tokens
|
||||||
value, measure_work, _completed_work, periods = escrow.functions.stakerInfo(staker1).call()
|
value, measure_work, _completed_work, periods = escrow.functions.stakerInfo(staker1).call()
|
||||||
assert not measure_work
|
assert not measure_work
|
||||||
|
@ -647,33 +624,45 @@ def test_reentrancy(testerchain, token_economics, deploy_contract, token, escrow
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.slow
|
@pytest.mark.slow
|
||||||
def test_max_allowed(testerchain, token_economics, deploy_contract, token, escrow):
|
def test_verifying_correctness(testerchain, token_economics, deploy_contract, token, escrow):
|
||||||
creator, bidder1, bidder2, *everyone_else = testerchain.w3.eth.accounts
|
creator, bidder1, bidder2, bidder3, *everyone_else = testerchain.w3.eth.accounts
|
||||||
|
gas_to_save_state = 30000
|
||||||
|
|
||||||
# Deploy WorkLock
|
# Deploy WorkLock
|
||||||
now = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp
|
|
||||||
start_bid_date = now
|
|
||||||
end_bid_date = start_bid_date + (60 * 60)
|
|
||||||
end_cancellation_date = end_bid_date
|
|
||||||
boosting_refund = 100
|
boosting_refund = 100
|
||||||
staking_periods = token_economics.minimum_locked_periods
|
staking_periods = token_economics.minimum_locked_periods
|
||||||
min_allowed_bid = to_wei(1, 'ether')
|
min_allowed_bid = to_wei(1, 'ether')
|
||||||
worklock, _ = deploy_contract(
|
|
||||||
contract_name='WorkLock',
|
def deploy_worklock(supply):
|
||||||
_token=token.address,
|
|
||||||
_escrow=escrow.address,
|
now = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp
|
||||||
_startBidDate=start_bid_date,
|
start_bid_date = now
|
||||||
_endBidDate=end_bid_date,
|
end_bid_date = start_bid_date + (60 * 60)
|
||||||
_endCancellationDate=end_cancellation_date,
|
end_cancellation_date = end_bid_date
|
||||||
_boostingRefund=boosting_refund,
|
|
||||||
_stakingPeriods=staking_periods,
|
contract, _ = deploy_contract(
|
||||||
_minAllowedBid=min_allowed_bid
|
contract_name='WorkLock',
|
||||||
)
|
_token=token.address,
|
||||||
|
_escrow=escrow.address,
|
||||||
|
_startBidDate=start_bid_date,
|
||||||
|
_endBidDate=end_bid_date,
|
||||||
|
_endCancellationDate=end_cancellation_date,
|
||||||
|
_boostingRefund=boosting_refund,
|
||||||
|
_stakingPeriods=staking_periods,
|
||||||
|
_minAllowedBid=min_allowed_bid
|
||||||
|
)
|
||||||
|
|
||||||
|
tx = token.functions.approve(contract.address, supply).transact()
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
tx = contract.functions.tokenDeposit(supply).transact()
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
log = contract.events.BiddersChecked.createFilter(fromBlock='latest')
|
||||||
|
|
||||||
|
return contract, log
|
||||||
|
|
||||||
|
# Test: bidder has too much tokens to claim
|
||||||
worklock_supply = token_economics.maximum_allowed_locked + 1
|
worklock_supply = token_economics.maximum_allowed_locked + 1
|
||||||
tx = token.functions.approve(worklock.address, worklock_supply).transact()
|
worklock, _checks_log = deploy_worklock(worklock_supply, min_allowed_bid)
|
||||||
testerchain.wait_for_receipt(tx)
|
|
||||||
tx = worklock.functions.tokenDeposit(worklock_supply).transact()
|
|
||||||
testerchain.wait_for_receipt(tx)
|
|
||||||
|
|
||||||
# Bid
|
# Bid
|
||||||
tx = testerchain.w3.eth.sendTransaction(
|
tx = testerchain.w3.eth.sendTransaction(
|
||||||
|
@ -685,6 +674,102 @@ def test_max_allowed(testerchain, token_economics, deploy_contract, token, escro
|
||||||
|
|
||||||
# Check will fail because bidder has too much tokens to claim
|
# Check will fail because bidder has too much tokens to claim
|
||||||
testerchain.time_travel(seconds=3600) # Wait exactly 1 hour
|
testerchain.time_travel(seconds=3600) # Wait exactly 1 hour
|
||||||
|
worklock_balance = testerchain.w3.eth.getBalance(worklock.address)
|
||||||
|
default_max = worklock.functions.maxAllowableLockedTokens().call()
|
||||||
|
assert default_max * worklock_balance // worklock_supply < min_allowed_bid
|
||||||
with pytest.raises((TransactionFailed, ValueError)):
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
tx = worklock.functions.verifyBiddingCorrectness(30000).transact()
|
tx = worklock.functions.verifyBiddingCorrectness(30000).transact()
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
# Test: bidder will get tokens as much as possible without force refund
|
||||||
|
worklock_supply = 3 * token_economics.maximum_allowed_locked
|
||||||
|
worklock, checks_log = deploy_worklock(worklock_supply, min_allowed_bid)
|
||||||
|
|
||||||
|
# Bids
|
||||||
|
for bidder in [bidder1, bidder2, bidder3]:
|
||||||
|
tx = testerchain.w3.eth.sendTransaction(
|
||||||
|
{'from': testerchain.etherbase_account, 'to': bidder, 'value': min_allowed_bid})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
tx = worklock.functions.bid().transact({'from': bidder, 'value': min_allowed_bid, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
assert worklock.functions.ethToTokens(min_allowed_bid).call() == token_economics.maximum_allowed_locked
|
||||||
|
|
||||||
|
# Wait exactly 1 hour
|
||||||
|
testerchain.time_travel(seconds=3600)
|
||||||
|
worklock_balance = testerchain.w3.eth.getBalance(worklock.address)
|
||||||
|
default_max = worklock.functions.defaultMaxAllowableLockedTokens().call()
|
||||||
|
assert default_max * worklock_balance // worklock_supply == min_allowed_bid
|
||||||
|
|
||||||
|
# Too low value for gas limit
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state)\
|
||||||
|
.transact({'from': bidder1, 'gas': gas_to_save_state + 20000})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state)\
|
||||||
|
.transact({'from': bidder1, 'gas': gas_to_save_state + 25000})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
assert worklock.functions.nextBidderToCheck().call() == 3
|
||||||
|
|
||||||
|
events = checks_log.get_all_entries()
|
||||||
|
assert 1 == len(events)
|
||||||
|
event_args = events[-1]['args']
|
||||||
|
assert event_args['sender'] == bidder1
|
||||||
|
assert event_args['startIndex'] == 0
|
||||||
|
assert event_args['endIndex'] == 3
|
||||||
|
|
||||||
|
# Test: partial verification with low amount of gas limit
|
||||||
|
worklock_supply = 3 * token_economics.maximum_allowed_locked
|
||||||
|
worklock, checks_log = deploy_worklock(worklock_supply)
|
||||||
|
|
||||||
|
# Bids
|
||||||
|
for bidder in [bidder1, bidder2, bidder3]:
|
||||||
|
tx = testerchain.w3.eth.sendTransaction(
|
||||||
|
{'from': testerchain.etherbase_account, 'to': bidder, 'value': min_allowed_bid})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
tx = worklock.functions.bid().transact({'from': bidder, 'value': min_allowed_bid, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
assert worklock.functions.ethToTokens(min_allowed_bid).call() == worklock_supply // 3
|
||||||
|
|
||||||
|
# Wait exactly 1 hour
|
||||||
|
testerchain.time_travel(seconds=3600) # Wait exactly 1 hour
|
||||||
|
worklock_balance = testerchain.w3.eth.getBalance(worklock.address)
|
||||||
|
default_max = worklock.functions.maxAllowableLockedTokens().call()
|
||||||
|
max_bid_from_max_stake = default_max * worklock_balance // worklock_supply
|
||||||
|
assert max_bid_from_max_stake >= min_allowed_bid
|
||||||
|
|
||||||
|
# Too low value for remaining gas
|
||||||
|
with pytest.raises((TransactionFailed, ValueError)):
|
||||||
|
tx = worklock.functions.verifyBiddingCorrectness(0)\
|
||||||
|
.transact({'from': bidder1, 'gas': gas_to_save_state + 25000})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
|
# Too low value for gas limit
|
||||||
|
assert worklock.functions.nextBidderToCheck().call() == 0
|
||||||
|
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact({'gas': gas_to_save_state + 25000})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
assert worklock.functions.nextBidderToCheck().call() == 0
|
||||||
|
|
||||||
|
# Set gas only for one check
|
||||||
|
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state)\
|
||||||
|
.transact({'gas': gas_to_save_state + 30000, 'gas_price': 0})
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
assert worklock.functions.nextBidderToCheck().call() == 1
|
||||||
|
|
||||||
|
events = checks_log.get_all_entries()
|
||||||
|
assert 1 == len(events)
|
||||||
|
event_args = events[-1]['args']
|
||||||
|
assert event_args['sender'] == creator
|
||||||
|
assert event_args['startIndex'] == 0
|
||||||
|
assert event_args['endIndex'] == 1
|
||||||
|
|
||||||
|
# Check others
|
||||||
|
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact()
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
assert worklock.functions.nextBidderToCheck().call() == 3
|
||||||
|
|
||||||
|
events = checks_log.get_all_entries()
|
||||||
|
assert 2 == len(events)
|
||||||
|
event_args = events[-1]['args']
|
||||||
|
assert event_args['sender'] == creator
|
||||||
|
assert event_args['startIndex'] == 1
|
||||||
|
assert event_args['endIndex'] == 3
|
||||||
|
|
|
@ -71,12 +71,14 @@ def test_verify_correctness(testerchain, agency, token_economics, test_registry)
|
||||||
# Wait until the cancellation window closes...
|
# Wait until the cancellation window closes...
|
||||||
testerchain.time_travel(seconds=token_economics.cancellation_window_duration+1)
|
testerchain.time_travel(seconds=token_economics.cancellation_window_duration+1)
|
||||||
|
|
||||||
assert not worklock_agent.is_claiming_available()
|
assert not worklock_agent.bidders_checked()
|
||||||
|
assert not worklock_agent.is_claiming_available(
|
||||||
with pytest.raises(Bidder.BidderError):
|
with pytest.raises(Bidder.BidderError):
|
||||||
_receipt = bidder.claim()
|
_receipt = bidder.claim()
|
||||||
|
|
||||||
receipts = bidder.verify_bidding_correctness(gas_limit=100000)
|
receipts = bidder.verify_bidding_correctness(gas_limit=100000)
|
||||||
assert worklock_agent.is_claiming_available()
|
assert worklock_agent.bidders_checked()
|
||||||
|
assert worklock_agent.is_claiming_available(
|
||||||
for iteration, receipt in receipts.items():
|
for iteration, receipt in receipts.items():
|
||||||
assert receipt['status'] == 1
|
assert receipt['status'] == 1
|
||||||
|
|
||||||
|
|
|
@ -102,8 +102,10 @@ def test_claim_before_checking(testerchain, agency, token_economics, test_regist
|
||||||
def test_verify_correctness(testerchain, agency, token_economics, test_registry):
|
def test_verify_correctness(testerchain, agency, token_economics, test_registry):
|
||||||
agent = ContractAgency.get_agent(WorkLockAgent, registry=test_registry)
|
agent = ContractAgency.get_agent(WorkLockAgent, registry=test_registry)
|
||||||
caller = testerchain.unassigned_accounts[0]
|
caller = testerchain.unassigned_accounts[0]
|
||||||
|
assert not agent.bidders_checked()
|
||||||
receipt = agent.verify_bidding_correctness(checksum_address=caller, gas_limit=100000)
|
receipt = agent.verify_bidding_correctness(checksum_address=caller, gas_limit=100000)
|
||||||
assert receipt['status'] == 1
|
assert receipt['status'] == 1
|
||||||
|
assert agent.bidders_checked()
|
||||||
assert agent.is_claiming_available()
|
assert agent.is_claiming_available()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ def test_cancel_bid(click_runner, testerchain, agency_local_registry, token_econ
|
||||||
assert not agent.get_deposited_eth(bidder) # No more bid
|
assert not agent.get_deposited_eth(bidder) # No more bid
|
||||||
|
|
||||||
|
|
||||||
def test_verify_correctness(click_runner, testerchain, agency_local_registry, token_economics):
|
def test_post_initialization(click_runner, testerchain, agency_local_registry, token_economics):
|
||||||
|
|
||||||
# Wait until the end of the cancellation period
|
# Wait until the end of the cancellation period
|
||||||
testerchain.time_travel(seconds=token_economics.cancellation_window_duration+2)
|
testerchain.time_travel(seconds=token_economics.cancellation_window_duration+2)
|
||||||
|
@ -123,8 +123,9 @@ def test_verify_correctness(click_runner, testerchain, agency_local_registry, to
|
||||||
bidder = testerchain.unassigned_accounts[0]
|
bidder = testerchain.unassigned_accounts[0]
|
||||||
agent = ContractAgency.get_agent(WorkLockAgent, registry=agency_local_registry)
|
agent = ContractAgency.get_agent(WorkLockAgent, registry=agency_local_registry)
|
||||||
assert not agent.is_claiming_available()
|
assert not agent.is_claiming_available()
|
||||||
|
assert not agent.bidders_checked()
|
||||||
|
|
||||||
command = ('verify-correctness',
|
command = ('post-initialization',
|
||||||
'--bidder-address', bidder,
|
'--bidder-address', bidder,
|
||||||
'--registry-filepath', agency_local_registry.filepath,
|
'--registry-filepath', agency_local_registry.filepath,
|
||||||
'--provider', TEST_PROVIDER_URI,
|
'--provider', TEST_PROVIDER_URI,
|
||||||
|
@ -136,6 +137,7 @@ def test_verify_correctness(click_runner, testerchain, agency_local_registry, to
|
||||||
result = click_runner.invoke(worklock, command, input=user_input, catch_exceptions=False)
|
result = click_runner.invoke(worklock, command, input=user_input, catch_exceptions=False)
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert agent.is_claiming_available()
|
assert agent.is_claiming_available()
|
||||||
|
assert agent.bidders_checked()
|
||||||
|
|
||||||
|
|
||||||
def test_claim(click_runner, testerchain, agency_local_registry, token_economics):
|
def test_claim(click_runner, testerchain, agency_local_registry, token_economics):
|
||||||
|
|
Loading…
Reference in New Issue