nucypher/tests/acceptance/cli/test_worklock_cli.py

289 lines
11 KiB
Python
Raw Normal View History

"""
This file is part of nucypher.
nucypher is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
nucypher is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
2020-04-14 19:48:12 +00:00
import random
import tempfile
import pytest
from eth_utils import to_wei
from web3 import Web3
from nucypher.crypto.powers import TransactingPower
from nucypher.blockchain.eth.actors import Bidder, Staker
from nucypher.blockchain.eth.agents import (
ContractAgency,
WorkLockAgent
)
from nucypher.blockchain.eth.token import NU
from nucypher.characters.lawful import Ursula
from nucypher.cli.commands.worklock import worklock
from tests.constants import (INSECURE_DEVELOPMENT_PASSWORD, MOCK_IP_ADDRESS, TEST_PROVIDER_URI)
from tests.utils.ursula import select_test_port
from nucypher.config.constants import TEMPORARY_DOMAIN
@pytest.fixture(scope='module')
def bids(testerchain):
bids_distribution = dict()
min_bid_eth_value = 1
max_bid_eth_value = 10
whale = testerchain.client.accounts[0]
bids_distribution[whale] = 50_000
for bidder in testerchain.client.accounts[1:12]:
bids_distribution[bidder] = random.randrange(min_bid_eth_value, max_bid_eth_value)
return bids_distribution
def test_status(click_runner, testerchain, agency_local_registry, token_economics):
command = ('status',
'--registry-filepath', agency_local_registry.filepath,
'--provider', TEST_PROVIDER_URI,
2020-04-14 19:48:12 +00:00
'--network', TEMPORARY_DOMAIN)
result = click_runner.invoke(worklock, command, catch_exceptions=False)
assert result.exit_code == 0
2020-04-14 19:48:12 +00:00
assert str(NU.from_nunits(token_economics.worklock_supply)) in result.output
assert str(Web3.fromWei(token_economics.worklock_min_allowed_bid, 'ether')) in result.output
def test_bid(click_runner, testerchain, agency_local_registry, token_economics, bids):
# Wait until biding window starts
testerchain.time_travel(seconds=90)
2020-07-08 14:28:58 +00:00
base_command = ('escrow',
'--registry-filepath', agency_local_registry.filepath,
'--provider', TEST_PROVIDER_URI,
2020-04-14 19:48:12 +00:00
'--network', TEMPORARY_DOMAIN,
'--force')
worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=agency_local_registry)
total_bids = 0
# Multiple bidders
for bidder, bid_eth_value in bids.items():
pre_bid_balance = testerchain.client.get_balance(bidder)
assert pre_bid_balance > to_wei(bid_eth_value, 'ether')
2020-07-08 14:28:58 +00:00
command = (*base_command, '--participant-address', bidder, '--value', bid_eth_value)
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + 'Y\n'
result = click_runner.invoke(worklock, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
post_bid_balance = testerchain.client.get_balance(bidder)
difference = pre_bid_balance - post_bid_balance
assert difference >= to_wei(bid_eth_value, 'ether')
total_bids += to_wei(bid_eth_value, 'ether')
assert testerchain.client.get_balance(worklock_agent.contract_address) == total_bids
def test_cancel_bid(click_runner, testerchain, agency_local_registry, token_economics, bids):
2020-02-24 20:36:36 +00:00
bidders = list(bids.keys())
bidder = bidders[-1]
agent = ContractAgency.get_agent(WorkLockAgent, registry=agency_local_registry)
2020-07-08 14:28:58 +00:00
command = ('cancel-escrow',
'--participant-address', bidder,
'--registry-filepath', agency_local_registry.filepath,
'--provider', TEST_PROVIDER_URI,
2020-04-14 19:48:12 +00:00
'--network', TEMPORARY_DOMAIN,
'--force')
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + 'Y\n'
result = click_runner.invoke(worklock, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
assert not agent.get_deposited_eth(bidder) # No more bid
2020-02-24 20:36:36 +00:00
# Wait until the end of the bidding period
testerchain.time_travel(seconds=token_economics.bidding_duration + 2)
bidder = bidders[-2]
2020-07-08 14:28:58 +00:00
command = ('cancel-escrow',
'--participant-address', bidder,
2020-02-24 20:36:36 +00:00
'--registry-filepath', agency_local_registry.filepath,
'--provider', TEST_PROVIDER_URI,
2020-04-14 19:48:12 +00:00
'--network', TEMPORARY_DOMAIN,
2020-02-24 20:36:36 +00:00
'--force')
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + 'Y\n'
result = click_runner.invoke(worklock, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
assert not agent.get_deposited_eth(bidder) # No more bid
def test_enable_claiming(click_runner, testerchain, agency_local_registry, token_economics):
2020-02-24 20:36:36 +00:00
# Wait until the end of the cancellation period
testerchain.time_travel(seconds=token_economics.cancellation_window_duration+2)
bidder = testerchain.client.accounts[0]
agent = ContractAgency.get_agent(WorkLockAgent, registry=agency_local_registry)
assert not agent.is_claiming_available()
assert not agent.bidders_checked()
command = ('enable-claiming',
2020-07-08 14:28:58 +00:00
'--participant-address', bidder,
'--registry-filepath', agency_local_registry.filepath,
'--provider', TEST_PROVIDER_URI,
'--force',
2020-04-14 19:48:12 +00:00
'--network', TEMPORARY_DOMAIN,
'--gas-limit', 100000)
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + 'Y\n'
result = click_runner.invoke(worklock, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
assert agent.is_claiming_available()
assert agent.bidders_checked()
def test_claim(click_runner, testerchain, agency_local_registry, token_economics):
agent = ContractAgency.get_agent(WorkLockAgent, registry=agency_local_registry)
bidder = testerchain.client.accounts[2]
command = ('claim',
2020-07-08 14:28:58 +00:00
'--participant-address', bidder,
'--registry-filepath', agency_local_registry.filepath,
'--provider', TEST_PROVIDER_URI,
2020-04-14 19:48:12 +00:00
'--network', TEMPORARY_DOMAIN,
'--force')
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + 'Y\n'
result = click_runner.invoke(worklock, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
whale = testerchain.client.accounts[0]
assert agent.get_available_compensation(checksum_address=whale) > 0
command = ('claim',
2020-07-08 14:28:58 +00:00
'--participant-address', whale,
'--registry-filepath', agency_local_registry.filepath,
'--provider', TEST_PROVIDER_URI,
2020-04-14 19:48:12 +00:00
'--network', TEMPORARY_DOMAIN,
'--force')
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + 'Y\n'
result = click_runner.invoke(worklock, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
assert agent.get_available_compensation(checksum_address=whale) == 0
# TODO: Check successful new stake in StakingEscrow
def test_remaining_work(click_runner, testerchain, agency_local_registry, token_economics):
bidder = testerchain.client.accounts[2]
# Ensure there is remaining work one layer below
worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=agency_local_registry)
remaining_work = worklock_agent.get_remaining_work(checksum_address=bidder)
assert remaining_work > 0
command = ('remaining-work',
2020-07-08 14:28:58 +00:00
'--participant-address', bidder,
'--registry-filepath', agency_local_registry.filepath,
'--provider', TEST_PROVIDER_URI,
2020-04-14 19:48:12 +00:00
'--network', TEMPORARY_DOMAIN)
result = click_runner.invoke(worklock, command, catch_exceptions=False)
assert result.exit_code == 0
# Ensure were displaying the bidder address and remaining work in the output
assert bidder in result.output
assert str(remaining_work) in result.output
def test_refund(click_runner, testerchain, agency_local_registry, token_economics):
bidder = testerchain.client.accounts[2]
worker_address = testerchain.unassigned_accounts[-1]
#
# WorkLock Staker-Worker
#
worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=agency_local_registry)
2020-01-15 18:29:58 +00:00
# Bidder is now STAKER. Bond a worker.
staker = Staker(is_me=True, checksum_address=bidder, registry=agency_local_registry)
receipt = staker.bond_worker(worker_address=worker_address)
assert receipt['status'] == 1
worker = Ursula(is_me=True,
registry=agency_local_registry,
checksum_address=bidder,
worker_address=worker_address,
rest_host=MOCK_IP_ADDRESS,
rest_port=select_test_port(),
db_filepath=tempfile.mkdtemp())
# Ensure there is work to do
remaining_work = worklock_agent.get_remaining_work(checksum_address=bidder)
assert remaining_work > 0
# Unlock
transacting_power = worker._crypto_power.power_ups(TransactingPower)
transacting_power.activate(password=INSECURE_DEVELOPMENT_PASSWORD)
# Do some work
for i in range(3):
2020-09-21 18:05:06 +00:00
txhash = worker.commit_to_next_period()
testerchain.wait_for_receipt(txhash)
assert receipt['status'] == 1
testerchain.time_travel(periods=1)
command = ('refund',
2020-07-08 14:28:58 +00:00
'--participant-address', bidder,
'--registry-filepath', agency_local_registry.filepath,
'--provider', TEST_PROVIDER_URI,
2020-04-14 19:48:12 +00:00
'--network', TEMPORARY_DOMAIN,
'--force')
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + 'Y\n'
result = click_runner.invoke(worklock, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
# Less work to do...
new_remaining_work = worklock_agent.get_remaining_work(checksum_address=bidder)
assert new_remaining_work < remaining_work
def test_participant_status(click_runner, testerchain, agency_local_registry, token_economics):
bidder = Bidder(checksum_address=testerchain.client.accounts[2], registry=agency_local_registry)
command = ('status',
'--registry-filepath', agency_local_registry.filepath,
2020-07-08 14:28:58 +00:00
'--participant-address', bidder.checksum_address,
'--provider', TEST_PROVIDER_URI,
2020-04-14 19:48:12 +00:00
'--network', TEMPORARY_DOMAIN)
result = click_runner.invoke(worklock, command, catch_exceptions=False)
assert result.exit_code == 0
# Bidder-specific data is displayed
assert bidder.checksum_address in result.output
assert str(bidder.remaining_work) in result.output
assert str(bidder.available_refund) in result.output
# Worklock economics are displayed
assert str(token_economics.worklock_boosting_refund_rate) in result.output
assert str(NU.from_nunits(token_economics.worklock_supply)) in result.output