mirror of https://github.com/nucypher/nucypher.git
commit
ede371eede
|
@ -25,7 +25,8 @@ General
|
||||||
* `NUCYPHER_STAKING_PROVIDERS_PAGINATION_SIZE_LIGHT_NODE`
|
* `NUCYPHER_STAKING_PROVIDERS_PAGINATION_SIZE_LIGHT_NODE`
|
||||||
Default pagination size for the maximum number of active staking providers to retrieve from PREApplication in
|
Default pagination size for the maximum number of active staking providers to retrieve from PREApplication in
|
||||||
one contract call when a light node provider is being used.
|
one contract call when a light node provider is being used.
|
||||||
|
* `NUCYPHER_STAKING_PROVIDER_ETH_PASSWORD`
|
||||||
|
Password for a staking provider's Keystore.
|
||||||
|
|
||||||
Alice
|
Alice
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -19,7 +19,7 @@ import random
|
||||||
import sys
|
import sys
|
||||||
from bisect import bisect_right
|
from bisect import bisect_right
|
||||||
from itertools import accumulate
|
from itertools import accumulate
|
||||||
from typing import Dict, Iterable, List, Tuple, Type, Union, Any, Optional, cast, Iterator, NamedTuple
|
from typing import Dict, Iterable, List, Tuple, Type, Union, Any, Optional, cast, NamedTuple
|
||||||
|
|
||||||
from constant_sorrow.constants import ( # type: ignore
|
from constant_sorrow.constants import ( # type: ignore
|
||||||
CONTRACT_CALL,
|
CONTRACT_CALL,
|
||||||
|
@ -64,8 +64,7 @@ from nucypher.types import (
|
||||||
StakingProviderInfo,
|
StakingProviderInfo,
|
||||||
PeriodDelta,
|
PeriodDelta,
|
||||||
StakingEscrowParameters,
|
StakingEscrowParameters,
|
||||||
PolicyInfo,
|
TuNits
|
||||||
ArrangementInfo, TuNits
|
|
||||||
)
|
)
|
||||||
from nucypher.utilities.logging import Logger # type: ignore
|
from nucypher.utilities.logging import Logger # type: ignore
|
||||||
|
|
||||||
|
@ -1124,9 +1123,9 @@ class PREApplicationAgent(EthereumContractAgent):
|
||||||
return receipt
|
return receipt
|
||||||
|
|
||||||
@contract_api(TRANSACTION)
|
@contract_api(TRANSACTION)
|
||||||
def bond_operator(self, provider: ChecksumAddress, operator: ChecksumAddress, transacting_power: TransactingPower) -> TxReceipt:
|
def bond_operator(self, staking_provider: ChecksumAddress, operator: ChecksumAddress, transacting_power: TransactingPower) -> TxReceipt:
|
||||||
"""For use by threshold operator accounts only."""
|
"""For use by threshold operator accounts only."""
|
||||||
contract_function: ContractFunction = self.contract.functions.bondOperator(provider, operator)
|
contract_function: ContractFunction = self.contract.functions.bondOperator(staking_provider, operator)
|
||||||
receipt = self.blockchain.send_transaction(contract_function=contract_function,
|
receipt = self.blockchain.send_transaction(contract_function=contract_function,
|
||||||
transacting_power=transacting_power)
|
transacting_power=transacting_power)
|
||||||
return receipt
|
return receipt
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
"""
|
||||||
|
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/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Tuple, Union
|
||||||
|
|
||||||
|
import click
|
||||||
|
import maya
|
||||||
|
from eth_typing import ChecksumAddress
|
||||||
|
|
||||||
|
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||||
|
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||||
|
from nucypher.blockchain.eth.signers import Signer
|
||||||
|
from nucypher.cli.actions.auth import get_client_password
|
||||||
|
from nucypher.cli.actions.select import select_network
|
||||||
|
from nucypher.cli.literature import (
|
||||||
|
STAKING_PROVIDER_UNAUTHORIZED,
|
||||||
|
BONDING_TIME,
|
||||||
|
ALREADY_BONDED,
|
||||||
|
UNEXPECTED_HUMAN_OPERATOR,
|
||||||
|
BONDING,
|
||||||
|
CONFIRM_BONDING,
|
||||||
|
NOT_BONDED,
|
||||||
|
CONFIRM_UNBONDING,
|
||||||
|
UNBONDING
|
||||||
|
)
|
||||||
|
from nucypher.cli.options import (
|
||||||
|
option_registry_filepath,
|
||||||
|
option_signer_uri,
|
||||||
|
option_provider_uri,
|
||||||
|
option_network,
|
||||||
|
option_staking_provider,
|
||||||
|
option_operator_address,
|
||||||
|
option_force
|
||||||
|
)
|
||||||
|
from nucypher.cli.painting.transactions import paint_receipt_summary
|
||||||
|
from nucypher.cli.utils import connect_to_blockchain, get_registry
|
||||||
|
from nucypher.config.constants import NUCYPHER_ENVVAR_STAKING_PROVIDER_ETH_PASSWORD
|
||||||
|
from nucypher.control.emitters import StdoutEmitter
|
||||||
|
from nucypher.crypto.powers import TransactingPower
|
||||||
|
|
||||||
|
|
||||||
|
def is_authorized(emitter, staking_provider: ChecksumAddress, agent: PREApplicationAgent) -> None:
|
||||||
|
_authorized = agent.is_authorized(staking_provider=staking_provider)
|
||||||
|
if not _authorized:
|
||||||
|
emitter.message(STAKING_PROVIDER_UNAUTHORIZED.format(provider=staking_provider), color='red')
|
||||||
|
raise click.Abort()
|
||||||
|
|
||||||
|
|
||||||
|
def is_bonded(agent, staking_provider: ChecksumAddress, return_address: bool = False) -> Union[bool, Tuple[bool, ChecksumAddress]]:
|
||||||
|
onchain_operator = agent.get_operator_from_staking_provider(staking_provider=staking_provider)
|
||||||
|
result = onchain_operator != NULL_ADDRESS
|
||||||
|
if not return_address:
|
||||||
|
return result
|
||||||
|
return result, onchain_operator
|
||||||
|
|
||||||
|
|
||||||
|
def check_bonding_requirements(emitter, agent: PREApplicationAgent, staking_provider: ChecksumAddress) -> None:
|
||||||
|
blockchain = agent.blockchain
|
||||||
|
now = blockchain.get_blocktime()
|
||||||
|
commencement = agent.get_staking_provider_info(staking_provider=staking_provider).operator_start_timestamp
|
||||||
|
min_seconds = agent.get_min_operator_seconds()
|
||||||
|
termination = (commencement + min_seconds)
|
||||||
|
if now < termination:
|
||||||
|
emitter.error(BONDING_TIME.format(date=maya.MayaDT(termination)))
|
||||||
|
raise click.Abort()
|
||||||
|
|
||||||
|
|
||||||
|
@click.command('bond')
|
||||||
|
@option_registry_filepath
|
||||||
|
@option_provider_uri(required=True)
|
||||||
|
@option_signer_uri
|
||||||
|
@option_operator_address
|
||||||
|
@option_staking_provider
|
||||||
|
@option_network(required=True)
|
||||||
|
@option_force
|
||||||
|
def bond(registry_filepath, provider_uri, signer_uri, operator_address, staking_provider, network, force):
|
||||||
|
"""
|
||||||
|
Bond an operator to a staking provider.
|
||||||
|
The staking provider must be authorized to use the PREApplication.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Setup
|
||||||
|
#
|
||||||
|
|
||||||
|
emitter = StdoutEmitter()
|
||||||
|
connect_to_blockchain(provider_uri=provider_uri, emitter=emitter)
|
||||||
|
if not signer_uri:
|
||||||
|
emitter.message('--signer is required', color='red')
|
||||||
|
raise click.Abort()
|
||||||
|
if not network:
|
||||||
|
network = select_network(emitter=emitter)
|
||||||
|
|
||||||
|
signer = Signer.from_signer_uri(signer_uri)
|
||||||
|
transacting_power = TransactingPower(account=staking_provider, signer=signer)
|
||||||
|
registry = get_registry(network=network, registry_filepath=registry_filepath)
|
||||||
|
agent = ContractAgency.get_agent(PREApplicationAgent, registry=registry)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Checks
|
||||||
|
#
|
||||||
|
|
||||||
|
# Check for authorization
|
||||||
|
is_authorized(emitter=emitter, agent=agent, staking_provider=staking_provider)
|
||||||
|
|
||||||
|
# Check bonding
|
||||||
|
if is_bonded(agent=agent, staking_provider=staking_provider, return_address=False):
|
||||||
|
# operator is already set - check timing
|
||||||
|
check_bonding_requirements(emitter=emitter, agent=agent, staking_provider=staking_provider)
|
||||||
|
|
||||||
|
# Check for pre-existing staking providers for this operator
|
||||||
|
onchain_staking_provider = agent.get_staking_provider_from_operator(operator_address=operator_address)
|
||||||
|
if onchain_staking_provider != NULL_ADDRESS:
|
||||||
|
emitter.message(ALREADY_BONDED.format(provider=onchain_staking_provider, operator=operator_address), color='red')
|
||||||
|
raise click.Abort() # dont steal bananas
|
||||||
|
|
||||||
|
# Check that operator is not human
|
||||||
|
if staking_provider != operator_address:
|
||||||
|
# if the operator has a beneficiary it is the staking provider.
|
||||||
|
beneficiary = agent.get_beneficiary(staking_provider=operator_address)
|
||||||
|
if beneficiary != NULL_ADDRESS:
|
||||||
|
emitter.message(UNEXPECTED_HUMAN_OPERATOR, color='red')
|
||||||
|
raise click.Abort()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bond
|
||||||
|
#
|
||||||
|
|
||||||
|
if not force:
|
||||||
|
click.confirm(CONFIRM_BONDING.format(provider=staking_provider, operator=operator_address), abort=True)
|
||||||
|
transacting_power.unlock(password=get_client_password(checksum_address=staking_provider, envvar=NUCYPHER_ENVVAR_STAKING_PROVIDER_ETH_PASSWORD))
|
||||||
|
emitter.echo(BONDING.format(operator=operator_address))
|
||||||
|
receipt = agent.bond_operator(operator=operator_address, transacting_power=transacting_power, staking_provider=staking_provider)
|
||||||
|
paint_receipt_summary(receipt=receipt, emitter=emitter)
|
||||||
|
|
||||||
|
|
||||||
|
@click.command('unbond')
|
||||||
|
@option_registry_filepath
|
||||||
|
@option_provider_uri(required=True)
|
||||||
|
@option_signer_uri
|
||||||
|
@option_staking_provider
|
||||||
|
@option_network()
|
||||||
|
@option_force
|
||||||
|
def unbond(registry_filepath, provider_uri, signer_uri, staking_provider, network, force):
|
||||||
|
"""Unbonds an operator from an authorized staking provider."""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Setup
|
||||||
|
#
|
||||||
|
|
||||||
|
emitter = StdoutEmitter()
|
||||||
|
if not signer_uri:
|
||||||
|
emitter.message('--signer is required', color='red')
|
||||||
|
raise click.Abort()
|
||||||
|
if not network:
|
||||||
|
network = select_network(emitter=emitter)
|
||||||
|
|
||||||
|
connect_to_blockchain(provider_uri=provider_uri, emitter=emitter)
|
||||||
|
registry = get_registry(network=network, registry_filepath=registry_filepath)
|
||||||
|
agent = ContractAgency.get_agent(PREApplicationAgent, registry=registry)
|
||||||
|
signer = Signer.from_signer_uri(signer_uri)
|
||||||
|
transacting_power = TransactingPower(account=staking_provider, signer=signer)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Check
|
||||||
|
#
|
||||||
|
|
||||||
|
bonded, onchain_operator_address = is_bonded(agent=agent, staking_provider=staking_provider, return_address=True)
|
||||||
|
if not bonded:
|
||||||
|
emitter.message(NOT_BONDED.format(provider=staking_provider), color='red')
|
||||||
|
raise click.Abort()
|
||||||
|
check_bonding_requirements(emitter=emitter, agent=agent, staking_provider=staking_provider)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Unbond
|
||||||
|
#
|
||||||
|
|
||||||
|
if not force:
|
||||||
|
click.confirm(CONFIRM_UNBONDING.format(provider=staking_provider, operator=onchain_operator_address), abort=True)
|
||||||
|
transacting_power.unlock(password=get_client_password(checksum_address=staking_provider, envvar=NUCYPHER_ENVVAR_STAKING_PROVIDER_ETH_PASSWORD))
|
||||||
|
emitter.echo(UNBONDING.format(operator=onchain_operator_address))
|
||||||
|
receipt = agent.bond_operator(operator=NULL_ADDRESS, transacting_power=transacting_power, staking_provider=staking_provider)
|
||||||
|
paint_receipt_summary(receipt=receipt, emitter=emitter)
|
|
@ -594,3 +594,26 @@ PORTER_CORS_ALLOWED_ORIGINS = "CORS Allow Origins: {allow_origins}"
|
||||||
PORTER_BOTH_TLS_KEY_AND_CERTIFICATION_MUST_BE_PROVIDED = "Both --tls-key-filepath and --tls-certificate-filepath must be provided to launch porter with TLS; only one specified"
|
PORTER_BOTH_TLS_KEY_AND_CERTIFICATION_MUST_BE_PROVIDED = "Both --tls-key-filepath and --tls-certificate-filepath must be provided to launch porter with TLS; only one specified"
|
||||||
|
|
||||||
PORTER_BASIC_AUTH_REQUIRES_HTTPS = "Basic authentication can only be used with HTTPS. --tls-key-filepath and --tls-certificate-filepath must also be provided"
|
PORTER_BASIC_AUTH_REQUIRES_HTTPS = "Basic authentication can only be used with HTTPS. --tls-key-filepath and --tls-certificate-filepath must also be provided"
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# PREApplication
|
||||||
|
#
|
||||||
|
|
||||||
|
STAKING_PROVIDER_UNAUTHORIZED = '{provider} is not authorized.'
|
||||||
|
|
||||||
|
CONFIRM_BONDING = 'Are you sure you want to bond staking provider {provider} to operator {operator}?'
|
||||||
|
|
||||||
|
BONDING_TIME = 'Bonding/Unbonding not permitted until {date}.'
|
||||||
|
|
||||||
|
ALREADY_BONDED = '{operator} is already bonded to {provider}'
|
||||||
|
|
||||||
|
BONDING = 'Bonding operator {operator}'
|
||||||
|
|
||||||
|
UNEXPECTED_HUMAN_OPERATOR = 'Operation not permitted'
|
||||||
|
|
||||||
|
UNBONDING = 'Unbonding operator {operator}'
|
||||||
|
|
||||||
|
CONFIRM_UNBONDING = 'Are you sure you want to unbond {operator} from {provider}?'
|
||||||
|
|
||||||
|
NOT_BONDED = '{provider} is not bonded to any operator'
|
||||||
|
|
|
@ -26,7 +26,8 @@ from nucypher.cli.commands import (
|
||||||
ursula,
|
ursula,
|
||||||
cloudworkers,
|
cloudworkers,
|
||||||
contacts,
|
contacts,
|
||||||
porter
|
porter,
|
||||||
|
bond,
|
||||||
)
|
)
|
||||||
from nucypher.cli.painting.help import echo_version, echo_config_root_path, echo_logging_root_path
|
from nucypher.cli.painting.help import echo_version, echo_config_root_path, echo_logging_root_path
|
||||||
|
|
||||||
|
@ -75,8 +76,12 @@ ENTRY_POINTS = (
|
||||||
ursula.ursula, # Untrusted Re-Encryption Proxy
|
ursula.ursula, # Untrusted Re-Encryption Proxy
|
||||||
stake.stake, # Stake Management
|
stake.stake, # Stake Management
|
||||||
|
|
||||||
|
# PRE Application
|
||||||
|
bond.bond,
|
||||||
|
bond.unbond,
|
||||||
|
|
||||||
# Utility Commands
|
# Utility Commands
|
||||||
status.status, # Network Status
|
status.status, # Network Status
|
||||||
cloudworkers.cloudworkers, # Remote Operator node management
|
cloudworkers.cloudworkers, # Remote Operator node management
|
||||||
contacts.contacts, # Character "card" management
|
contacts.contacts, # Character "card" management
|
||||||
porter.porter
|
porter.porter
|
||||||
|
|
|
@ -55,6 +55,7 @@ option_hw_wallet = click.option('--hw-wallet/--no-hw-wallet')
|
||||||
option_light = click.option('--light', help="Indicate that node is light", is_flag=True, default=None)
|
option_light = click.option('--light', help="Indicate that node is light", is_flag=True, default=None)
|
||||||
option_lonely = click.option('--lonely', help="Do not connect to seednodes", is_flag=True)
|
option_lonely = click.option('--lonely', help="Do not connect to seednodes", is_flag=True)
|
||||||
option_min_stake = click.option('--min-stake', help="The minimum stake the teacher must have to be locally accepted.", type=STAKED_TOKENS_RANGE, default=MIN_AUTHORIZATION)
|
option_min_stake = click.option('--min-stake', help="The minimum stake the teacher must have to be locally accepted.", type=STAKED_TOKENS_RANGE, default=MIN_AUTHORIZATION)
|
||||||
|
option_operator_address = click.option('--operator-address', help="Address to bond as an operator", type=EIP55_CHECKSUM_ADDRESS, required=True)
|
||||||
option_parameters = click.option('--parameters', help="Filepath to a JSON file containing additional parameters", type=EXISTING_READABLE_FILE)
|
option_parameters = click.option('--parameters', help="Filepath to a JSON file containing additional parameters", type=EXISTING_READABLE_FILE)
|
||||||
option_participant_address = click.option('--participant-address', help="Participant's checksum address.", type=EIP55_CHECKSUM_ADDRESS)
|
option_participant_address = click.option('--participant-address', help="Participant's checksum address.", type=EIP55_CHECKSUM_ADDRESS)
|
||||||
option_payment_provider = click.option('--payment-provider', help="Connection URL for payment method", type=click.STRING, required=False)
|
option_payment_provider = click.option('--payment-provider', help="Connection URL for payment method", type=click.STRING, required=False)
|
||||||
|
@ -65,6 +66,7 @@ option_registry_filepath = click.option('--registry-filepath', help="Custom cont
|
||||||
option_shares = click.option('--shares', '-n', help="N-Total shares", type=click.INT)
|
option_shares = click.option('--shares', '-n', help="N-Total shares", type=click.INT)
|
||||||
option_signer_uri = click.option('--signer', 'signer_uri', '-S', default=None, type=str)
|
option_signer_uri = click.option('--signer', 'signer_uri', '-S', default=None, type=str)
|
||||||
option_staking_address = click.option('--staking-address', help="Address of a NuCypher staker", type=EIP55_CHECKSUM_ADDRESS)
|
option_staking_address = click.option('--staking-address', help="Address of a NuCypher staker", type=EIP55_CHECKSUM_ADDRESS)
|
||||||
|
option_staking_provider = click.option('--staking-provider', help="Staking provider ethereum address", type=EIP55_CHECKSUM_ADDRESS, required=True)
|
||||||
option_teacher_uri = click.option('--teacher', 'teacher_uri', help="An Ursula URI to start learning from (seednode)", type=click.STRING)
|
option_teacher_uri = click.option('--teacher', 'teacher_uri', help="An Ursula URI to start learning from (seednode)", type=click.STRING)
|
||||||
option_threshold = click.option('--threshold', '-m', help="M-Threshold KFrags", type=click.INT)
|
option_threshold = click.option('--threshold', '-m', help="M-Threshold KFrags", type=click.INT)
|
||||||
option_treasure_map = click.option('--treasure-map', 'treasure_map', help="Encrypted treasure map as base64 for retrieval", type=click.STRING, required=True)
|
option_treasure_map = click.option('--treasure-map', 'treasure_map', help="Encrypted treasure map as base64 for retrieval", type=click.STRING, required=True)
|
||||||
|
|
|
@ -30,6 +30,7 @@ import nucypher
|
||||||
NUCYPHER_ENVVAR_KEYSTORE_PASSWORD = "NUCYPHER_KEYSTORE_PASSWORD"
|
NUCYPHER_ENVVAR_KEYSTORE_PASSWORD = "NUCYPHER_KEYSTORE_PASSWORD"
|
||||||
NUCYPHER_ENVVAR_OPERATOR_ADDRESS = "NUCYPHER_OPERATOR_ADDRESS"
|
NUCYPHER_ENVVAR_OPERATOR_ADDRESS = "NUCYPHER_OPERATOR_ADDRESS"
|
||||||
NUCYPHER_ENVVAR_OPERATOR_ETH_PASSWORD = "NUCYPHER_WORKER_ETH_PASSWORD"
|
NUCYPHER_ENVVAR_OPERATOR_ETH_PASSWORD = "NUCYPHER_WORKER_ETH_PASSWORD"
|
||||||
|
NUCYPHER_ENVVAR_STAKING_PROVIDER_ETH_PASSWORD = "NUCYPHER_STAKING_PROVIDER_ETH_PASSWORD"
|
||||||
NUCYPHER_ENVVAR_ALICE_ETH_PASSWORD = "NUCYPHER_ALICE_ETH_PASSWORD"
|
NUCYPHER_ENVVAR_ALICE_ETH_PASSWORD = "NUCYPHER_ALICE_ETH_PASSWORD"
|
||||||
NUCYPHER_ENVVAR_BOB_ETH_PASSWORD = "NUCYPHER_BOB_ETH_PASSWORD"
|
NUCYPHER_ENVVAR_BOB_ETH_PASSWORD = "NUCYPHER_BOB_ETH_PASSWORD"
|
||||||
NUCYPHER_ENVVAR_PROVIDER_URI = "NUCYPHER_PROVIDER_URI"
|
NUCYPHER_ENVVAR_PROVIDER_URI = "NUCYPHER_PROVIDER_URI"
|
||||||
|
|
|
@ -212,7 +212,7 @@ def test_ursula_operator_confirmation(ursula_decentralized_test_config,
|
||||||
|
|
||||||
# now lets visit stake.nucypher.network and bond this operator
|
# now lets visit stake.nucypher.network and bond this operator
|
||||||
tpower = TransactingPower(account=staking_provider, signer=Web3Signer(testerchain.client))
|
tpower = TransactingPower(account=staking_provider, signer=Web3Signer(testerchain.client))
|
||||||
application_agent.bond_operator(provider=staking_provider,
|
application_agent.bond_operator(staking_provider=staking_provider,
|
||||||
operator=operator_address,
|
operator=operator_address,
|
||||||
transacting_power=tpower)
|
transacting_power=tpower)
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ def test_ursula_operator_confirmation_autopilot(mocker,
|
||||||
|
|
||||||
# now lets bond this worker
|
# now lets bond this worker
|
||||||
tpower = TransactingPower(account=staking_provider2, signer=Web3Signer(testerchain.client))
|
tpower = TransactingPower(account=staking_provider2, signer=Web3Signer(testerchain.client))
|
||||||
application_agent.bond_operator(provider=staking_provider2,
|
application_agent.bond_operator(staking_provider=staking_provider2,
|
||||||
operator=operator2,
|
operator=operator2,
|
||||||
transacting_power=tpower)
|
transacting_power=tpower)
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ def test_staking_providers_and_operators_relationships(testerchain,
|
||||||
|
|
||||||
tpower = TransactingPower(account=staking_provider_account, signer=Web3Signer(testerchain.client))
|
tpower = TransactingPower(account=staking_provider_account, signer=Web3Signer(testerchain.client))
|
||||||
_txhash = application_agent.bond_operator(transacting_power=tpower,
|
_txhash = application_agent.bond_operator(transacting_power=tpower,
|
||||||
provider=staking_provider_account,
|
staking_provider=staking_provider_account,
|
||||||
operator=operator_account)
|
operator=operator_account)
|
||||||
|
|
||||||
# We can check the staker-worker relation from both sides
|
# We can check the staker-worker relation from both sides
|
||||||
|
|
|
@ -52,7 +52,7 @@ def test_sampling_distribution(testerchain, test_registry, threshold_staking, ap
|
||||||
power = TransactingPower(account=provider_address, signer=Web3Signer(testerchain.client))
|
power = TransactingPower(account=provider_address, signer=Web3Signer(testerchain.client))
|
||||||
|
|
||||||
# We assume that the staking provider knows in advance the account of her operator
|
# We assume that the staking provider knows in advance the account of her operator
|
||||||
application_agent.bond_operator(provider=provider_address,
|
application_agent.bond_operator(staking_provider=provider_address,
|
||||||
operator=operator_address,
|
operator=operator_address,
|
||||||
transacting_power=power)
|
transacting_power=power)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
"""
|
||||||
|
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/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from eth_typing import ChecksumAddress
|
||||||
|
|
||||||
|
from nucypher.cli.commands.bond import bond, unbond
|
||||||
|
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||||
|
from tests.constants import TEST_PROVIDER_URI, INSECURE_DEVELOPMENT_PASSWORD
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='module')
|
||||||
|
def operator_address(testerchain):
|
||||||
|
return testerchain.unassigned_accounts.pop(1)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='module')
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'agency')
|
||||||
|
def staking_provider_address(testerchain):
|
||||||
|
return testerchain.unassigned_accounts.pop(1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_nucypher_bond_help(click_runner, testerchain):
|
||||||
|
command = '--help'
|
||||||
|
result = click_runner.invoke(bond, command, catch_exceptions=False)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='module')
|
||||||
|
def authorized_staking_provider(testerchain, threshold_staking, staking_provider_address, application_economics):
|
||||||
|
# initialize threshold stake
|
||||||
|
tx = threshold_staking.functions.setRoles(staking_provider_address).transact()
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
tx = threshold_staking.functions.setStakes(staking_provider_address, application_economics.min_authorization, 0, 0).transact()
|
||||||
|
testerchain.wait_for_receipt(tx)
|
||||||
|
return staking_provider_address
|
||||||
|
|
||||||
|
|
||||||
|
def exec_bond(click_runner, operator_address: ChecksumAddress, staking_provider_address: ChecksumAddress):
|
||||||
|
command = ('--operator-address', operator_address,
|
||||||
|
'--staking-provider', staking_provider_address,
|
||||||
|
'--provider', TEST_PROVIDER_URI,
|
||||||
|
'--network', TEMPORARY_DOMAIN,
|
||||||
|
'--signer', TEST_PROVIDER_URI,
|
||||||
|
'--force')
|
||||||
|
result = click_runner.invoke(bond,
|
||||||
|
command,
|
||||||
|
catch_exceptions=False,
|
||||||
|
env=dict(NUCYPHER_STAKING_PROVIDER_ETH_PASSWORD=INSECURE_DEVELOPMENT_PASSWORD))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def exec_unbond(click_runner, staking_provider_address: ChecksumAddress):
|
||||||
|
command = ('--staking-provider', staking_provider_address,
|
||||||
|
'--provider', TEST_PROVIDER_URI,
|
||||||
|
'--network', TEMPORARY_DOMAIN,
|
||||||
|
'--signer', TEST_PROVIDER_URI,
|
||||||
|
'--force')
|
||||||
|
result = click_runner.invoke(unbond,
|
||||||
|
command,
|
||||||
|
catch_exceptions=False,
|
||||||
|
env=dict(NUCYPHER_STAKING_PROVIDER_ETH_PASSWORD=INSECURE_DEVELOPMENT_PASSWORD))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'agency')
|
||||||
|
def test_nucypher_bond_unauthorized(click_runner, testerchain, operator_address, staking_provider_address):
|
||||||
|
result = exec_bond(
|
||||||
|
click_runner=click_runner,
|
||||||
|
operator_address=operator_address,
|
||||||
|
staking_provider_address=staking_provider_address
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
error_message = f'{staking_provider_address} is not authorized'
|
||||||
|
assert error_message in result.output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'agency', 'test_registry')
|
||||||
|
def test_nucypher_bond(click_runner, testerchain, operator_address, authorized_staking_provider):
|
||||||
|
result = exec_bond(
|
||||||
|
click_runner=click_runner,
|
||||||
|
operator_address=operator_address,
|
||||||
|
staking_provider_address=authorized_staking_provider
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'agency')
|
||||||
|
def test_nucypher_rebond_too_soon(click_runner, testerchain, operator_address, staking_provider_address):
|
||||||
|
result = exec_bond(
|
||||||
|
click_runner=click_runner,
|
||||||
|
operator_address=operator_address,
|
||||||
|
staking_provider_address=staking_provider_address
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
error_message = 'Bonding/Unbonding not permitted until '
|
||||||
|
assert error_message in result.output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'agency')
|
||||||
|
def test_nucypher_rebond_operator(click_runner,
|
||||||
|
testerchain,
|
||||||
|
operator_address,
|
||||||
|
staking_provider_address,
|
||||||
|
application_economics):
|
||||||
|
testerchain.time_travel(seconds=application_economics.min_operator_seconds)
|
||||||
|
result = exec_bond(
|
||||||
|
click_runner=click_runner,
|
||||||
|
operator_address=testerchain.unassigned_accounts[-1],
|
||||||
|
staking_provider_address=staking_provider_address
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'agency')
|
||||||
|
def test_nucypher_unbond_operator(click_runner,
|
||||||
|
testerchain,
|
||||||
|
staking_provider_address,
|
||||||
|
application_economics):
|
||||||
|
testerchain.time_travel(seconds=application_economics.min_operator_seconds)
|
||||||
|
result = exec_unbond(click_runner=click_runner, staking_provider_address=staking_provider_address)
|
||||||
|
assert result.exit_code == 0
|
|
@ -72,7 +72,7 @@ def mock_funded_account_password_keystore(tmp_path_factory, testerchain, thresho
|
||||||
provider_power.unlock(password=INSECURE_DEVELOPMENT_PASSWORD)
|
provider_power.unlock(password=INSECURE_DEVELOPMENT_PASSWORD)
|
||||||
|
|
||||||
pre_application_agent = ContractAgency.get_agent(PREApplicationAgent, registry=test_registry)
|
pre_application_agent = ContractAgency.get_agent(PREApplicationAgent, registry=test_registry)
|
||||||
pre_application_agent.bond_operator(provider=provider_address,
|
pre_application_agent.bond_operator(staking_provider=provider_address,
|
||||||
operator=account.address,
|
operator=account.address,
|
||||||
transacting_power=provider_power)
|
transacting_power=provider_power)
|
||||||
|
|
||||||
|
|
|
@ -632,7 +632,7 @@ def staking_providers(testerchain, agency, test_registry, threshold_staking):
|
||||||
testerchain.wait_for_receipt(tx)
|
testerchain.wait_for_receipt(tx)
|
||||||
|
|
||||||
# We assume that the staking provider knows in advance the account of her operator
|
# We assume that the staking provider knows in advance the account of her operator
|
||||||
pre_application_agent.bond_operator(provider=provider_address,
|
pre_application_agent.bond_operator(staking_provider=provider_address,
|
||||||
operator=operator_address,
|
operator=operator_address,
|
||||||
transacting_power=provider_power)
|
transacting_power=provider_power)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,216 @@
|
||||||
|
"""
|
||||||
|
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/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import maya
|
||||||
|
import pytest
|
||||||
|
from eth_typing import ChecksumAddress
|
||||||
|
|
||||||
|
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||||
|
from nucypher.cli.commands.bond import unbond, bond
|
||||||
|
from nucypher.cli.literature import UNEXPECTED_HUMAN_OPERATOR, BONDING_TIME, ALREADY_BONDED
|
||||||
|
from nucypher.config.constants import (
|
||||||
|
TEMPORARY_DOMAIN,
|
||||||
|
NUCYPHER_ENVVAR_STAKING_PROVIDER_ETH_PASSWORD
|
||||||
|
)
|
||||||
|
from nucypher.crypto.powers import TransactingPower
|
||||||
|
from nucypher.types import StakingProviderInfo
|
||||||
|
from tests.constants import TEST_PROVIDER_URI, INSECURE_DEVELOPMENT_PASSWORD
|
||||||
|
|
||||||
|
cli_env = {NUCYPHER_ENVVAR_STAKING_PROVIDER_ETH_PASSWORD: INSECURE_DEVELOPMENT_PASSWORD}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='module', autouse=True)
|
||||||
|
def mock_transacting_power(module_mocker):
|
||||||
|
module_mocker.patch.object(TransactingPower, 'unlock')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='module')
|
||||||
|
def operator_address(mock_testerchain):
|
||||||
|
return mock_testerchain.unassigned_accounts[1]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='module')
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'mock_contract_agency')
|
||||||
|
def staking_provider_address(mock_testerchain):
|
||||||
|
return mock_testerchain.unassigned_accounts[2]
|
||||||
|
|
||||||
|
|
||||||
|
def test_nucypher_bond_help(click_runner, mock_testerchain):
|
||||||
|
command = '--help'
|
||||||
|
result = click_runner.invoke(bond, command, catch_exceptions=False)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
def exec_bond(click_runner, operator_address: ChecksumAddress, staking_provider_address: ChecksumAddress):
|
||||||
|
command = ('--operator-address', operator_address,
|
||||||
|
'--staking-provider', staking_provider_address,
|
||||||
|
'--provider', TEST_PROVIDER_URI,
|
||||||
|
'--network', TEMPORARY_DOMAIN,
|
||||||
|
'--signer', TEST_PROVIDER_URI,
|
||||||
|
'--force' # non-interactive only
|
||||||
|
)
|
||||||
|
result = click_runner.invoke(bond, command, catch_exceptions=False, env=cli_env)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def exec_unbond(click_runner, staking_provider_address: ChecksumAddress):
|
||||||
|
command = ('--staking-provider', staking_provider_address,
|
||||||
|
'--provider', TEST_PROVIDER_URI,
|
||||||
|
'--network', TEMPORARY_DOMAIN,
|
||||||
|
'--signer', TEST_PROVIDER_URI,
|
||||||
|
'--force' # non-interactive only
|
||||||
|
)
|
||||||
|
result = click_runner.invoke(unbond, command, catch_exceptions=False, env=cli_env)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'mock_contract_agency', 'patch_keystore')
|
||||||
|
def test_nucypher_bond_unauthorized(click_runner, mock_testerchain, operator_address, staking_provider_address, mock_application_agent):
|
||||||
|
|
||||||
|
mock_application_agent.is_authorized.return_value = False
|
||||||
|
mock_application_agent.get_staking_provider_info.return_value = StakingProviderInfo(
|
||||||
|
operator=NULL_ADDRESS,
|
||||||
|
operator_confirmed=False,
|
||||||
|
operator_start_timestamp=1
|
||||||
|
)
|
||||||
|
|
||||||
|
result = exec_bond(
|
||||||
|
click_runner=click_runner,
|
||||||
|
operator_address=operator_address,
|
||||||
|
staking_provider_address=staking_provider_address
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
error_message = f'{staking_provider_address} is not authorized'
|
||||||
|
assert error_message in result.output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'mock_contract_agency', 'test_registry')
|
||||||
|
def test_nucypher_unexpected_beneficiary(click_runner, mock_testerchain, operator_address, staking_provider_address, mock_application_agent):
|
||||||
|
|
||||||
|
mock_application_agent.get_staking_provider_info.return_value = StakingProviderInfo(
|
||||||
|
operator=NULL_ADDRESS,
|
||||||
|
operator_confirmed=False,
|
||||||
|
operator_start_timestamp=1
|
||||||
|
)
|
||||||
|
mock_application_agent.get_beneficiary.return_value = mock_testerchain.unassigned_accounts[-1]
|
||||||
|
mock_application_agent.get_staking_provider_from_operator.return_value = NULL_ADDRESS
|
||||||
|
|
||||||
|
result = exec_bond(
|
||||||
|
click_runner=click_runner,
|
||||||
|
operator_address=operator_address,
|
||||||
|
staking_provider_address=staking_provider_address
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.exit_code == 1
|
||||||
|
assert UNEXPECTED_HUMAN_OPERATOR in result.output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'mock_contract_agency', 'test_registry')
|
||||||
|
def test_nucypher_bond(click_runner, mock_testerchain, operator_address, staking_provider_address, mock_application_agent):
|
||||||
|
|
||||||
|
mock_application_agent.get_staking_provider_info.return_value = StakingProviderInfo(
|
||||||
|
operator=NULL_ADDRESS,
|
||||||
|
operator_confirmed=False,
|
||||||
|
operator_start_timestamp=1
|
||||||
|
)
|
||||||
|
mock_application_agent.get_beneficiary.return_value = NULL_ADDRESS
|
||||||
|
mock_application_agent.get_staking_provider_from_operator.return_value = NULL_ADDRESS
|
||||||
|
|
||||||
|
result = exec_bond(
|
||||||
|
click_runner=click_runner,
|
||||||
|
operator_address=operator_address,
|
||||||
|
staking_provider_address=staking_provider_address
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'mock_contract_agency')
|
||||||
|
def test_nucypher_unbond_operator(click_runner, mock_testerchain, staking_provider_address, mock_application_agent, operator_address):
|
||||||
|
|
||||||
|
mock_application_agent.get_staking_provider_info.return_value = StakingProviderInfo(
|
||||||
|
operator=operator_address,
|
||||||
|
operator_confirmed=False,
|
||||||
|
operator_start_timestamp=1
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_application_agent.get_staking_provider_from_operator.return_value = staking_provider_address
|
||||||
|
|
||||||
|
result = exec_unbond(click_runner=click_runner, staking_provider_address=staking_provider_address)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'mock_contract_agency')
|
||||||
|
def test_nucypher_rebond_too_soon(click_runner, mock_testerchain, operator_address, staking_provider_address, mock_application_agent):
|
||||||
|
|
||||||
|
min_authorized_seconds = 5
|
||||||
|
now = mock_testerchain.get_blocktime()
|
||||||
|
operator_start_timestamp = now
|
||||||
|
termination = operator_start_timestamp + min_authorized_seconds
|
||||||
|
|
||||||
|
mock_application_agent.get_staking_provider_info.return_value = StakingProviderInfo(
|
||||||
|
operator=operator_address,
|
||||||
|
operator_confirmed=False,
|
||||||
|
operator_start_timestamp=operator_start_timestamp
|
||||||
|
)
|
||||||
|
mock_application_agent.get_min_operator_seconds.return_value = min_authorized_seconds
|
||||||
|
|
||||||
|
result = exec_bond(
|
||||||
|
click_runner=click_runner,
|
||||||
|
operator_address=operator_address,
|
||||||
|
staking_provider_address=staking_provider_address
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
error_message = BONDING_TIME.format(date=maya.MayaDT(termination))
|
||||||
|
assert error_message in result.output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'mock_contract_agency')
|
||||||
|
def test_nucypher_bond_already_claimed_operator(click_runner, mock_testerchain, operator_address, staking_provider_address, mock_application_agent):
|
||||||
|
mock_application_agent.get_staking_provider_info.return_value = StakingProviderInfo(
|
||||||
|
operator=NULL_ADDRESS,
|
||||||
|
operator_confirmed=False,
|
||||||
|
operator_start_timestamp=1
|
||||||
|
)
|
||||||
|
mock_application_agent.get_beneficiary.return_value = NULL_ADDRESS
|
||||||
|
mock_application_agent.get_operator_from_staking_provider.return_value = NULL_ADDRESS
|
||||||
|
mock_application_agent.get_staking_provider_from_operator.return_value = mock_testerchain.unassigned_accounts[4]
|
||||||
|
|
||||||
|
result = exec_bond(
|
||||||
|
click_runner=click_runner,
|
||||||
|
operator_address=operator_address,
|
||||||
|
staking_provider_address=staking_provider_address
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('test_registry_source_manager', 'mock_contract_agency')
|
||||||
|
def test_nucypher_rebond_operator(click_runner, mock_testerchain, operator_address, staking_provider_address, mock_application_agent):
|
||||||
|
mock_application_agent.get_staking_provider_info.return_value = StakingProviderInfo(
|
||||||
|
operator=mock_testerchain.unassigned_accounts[-1],
|
||||||
|
operator_confirmed=False,
|
||||||
|
operator_start_timestamp=1
|
||||||
|
)
|
||||||
|
mock_application_agent.get_beneficiary.return_value = NULL_ADDRESS
|
||||||
|
mock_application_agent.get_staking_provider_from_operator.return_value = NULL_ADDRESS
|
||||||
|
|
||||||
|
result = exec_bond(
|
||||||
|
click_runner=click_runner,
|
||||||
|
operator_address=operator_address,
|
||||||
|
staking_provider_address=staking_provider_address
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
|
@ -24,7 +24,7 @@ from nucypher.blockchain.eth.agents import (
|
||||||
AdjudicatorAgent,
|
AdjudicatorAgent,
|
||||||
ContractAgency,
|
ContractAgency,
|
||||||
NucypherTokenAgent,
|
NucypherTokenAgent,
|
||||||
StakingEscrowAgent
|
StakingEscrowAgent, PREApplicationAgent
|
||||||
)
|
)
|
||||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
||||||
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
|
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
|
||||||
|
@ -74,6 +74,17 @@ def mock_staking_agent(mock_testerchain, application_economics, mock_contract_ag
|
||||||
mock_agent.reset()
|
mock_agent.reset()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function', autouse=True)
|
||||||
|
def mock_application_agent(mock_testerchain, application_economics, mock_contract_agency, mocker):
|
||||||
|
mock_agent = mock_contract_agency.get_agent(PREApplicationAgent)
|
||||||
|
|
||||||
|
# Handle the special case of commit_to_next_period, which returns a txhash due to the fire_and_forget option
|
||||||
|
mock_agent.confirm_operator_address = mocker.Mock(return_value=MockContractAgent.FAKE_TX_HASH)
|
||||||
|
|
||||||
|
yield mock_agent
|
||||||
|
mock_agent.reset()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function', autouse=True)
|
@pytest.fixture(scope='function', autouse=True)
|
||||||
def mock_adjudicator_agent(mock_testerchain, application_economics, mock_contract_agency):
|
def mock_adjudicator_agent(mock_testerchain, application_economics, mock_contract_agency):
|
||||||
mock_agent = mock_contract_agency.get_agent(AdjudicatorAgent)
|
mock_agent = mock_contract_agency.get_agent(AdjudicatorAgent)
|
||||||
|
@ -235,7 +246,7 @@ def bob_blockchain_test_config(mock_testerchain, test_registry):
|
||||||
def ursula_decentralized_test_config(mock_testerchain, test_registry):
|
def ursula_decentralized_test_config(mock_testerchain, test_registry):
|
||||||
config = make_ursula_test_configuration(federated=False,
|
config = make_ursula_test_configuration(federated=False,
|
||||||
provider_uri=MOCK_PROVIDER_URI, # L1
|
provider_uri=MOCK_PROVIDER_URI, # L1
|
||||||
payment_provider=MOCK_PROVIDER_URI, # L2
|
payment_provider=MOCK_PROVIDER_URI, # L1/L2
|
||||||
test_registry=test_registry,
|
test_registry=test_registry,
|
||||||
rest_port=MOCK_URSULA_STARTING_PORT,
|
rest_port=MOCK_URSULA_STARTING_PORT,
|
||||||
checksum_address=mock_testerchain.ursula_account(index=0))
|
checksum_address=mock_testerchain.ursula_account(index=0))
|
||||||
|
|
Loading…
Reference in New Issue