WorkLock: small refactoring of methods and tests

pull/1773/head
vzotova 2020-02-28 21:32:08 +03:00
parent 8571f220f4
commit 41cbd542ee
10 changed files with 194 additions and 90 deletions

View File

@ -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

View File

@ -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()

View File

@ -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);

View File

@ -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,

View File

@ -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;
} }

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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):