mirror of https://github.com/nucypher/nucypher.git
commit
84d8344236
|
@ -84,9 +84,9 @@ conditions = {
|
|||
}
|
||||
|
||||
message = "hello world".encode()
|
||||
ciphertext = enrico.encrypt_for_dkg(plaintext=message, conditions=conditions)
|
||||
threshold_message_kit = enrico.encrypt_for_dkg(plaintext=message, conditions=conditions)
|
||||
|
||||
print(f"Encrypted message: {bytes(ciphertext).hex()}")
|
||||
print(f"Encrypted message: {bytes(threshold_message_kit).hex()}")
|
||||
|
||||
###############
|
||||
# Bob
|
||||
|
@ -103,10 +103,6 @@ bob = Bob(
|
|||
|
||||
bob.start_learning_loop(now=True)
|
||||
|
||||
cleartext = bob.threshold_decrypt(
|
||||
ritual_id=ritual_id,
|
||||
ciphertext=ciphertext,
|
||||
conditions=conditions,
|
||||
)
|
||||
cleartext = bob.threshold_decrypt(threshold_message_kit=threshold_message_kit)
|
||||
|
||||
print(bytes(cleartext).decode())
|
||||
|
|
|
@ -26,7 +26,6 @@ threshold_message_kit = enrico.encrypt_for_dkg(
|
|||
)
|
||||
|
||||
cleartext_from_ciphertext = bob.threshold_decrypt(
|
||||
ritual_id=ANYTHING_CAN_BE_PASSED_AS_RITUAL_ID,
|
||||
threshold_message_kit=threshold_message_kit,
|
||||
)
|
||||
|
||||
|
|
|
@ -78,7 +78,6 @@ bob = Bob(
|
|||
bob.start_learning_loop(now=True)
|
||||
|
||||
cleartext = bob.threshold_decrypt(
|
||||
ritual_id=ritual_id,
|
||||
threshold_message_kit=threshold_message_kit,
|
||||
)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import Optional, Tuple
|
|||
from web3 import Web3
|
||||
from web3.types import Wei
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from nucypher.blockchain.eth.registry import BaseContractRegistry
|
||||
from nucypher.blockchain.eth.token import TToken
|
||||
|
||||
|
@ -61,7 +61,7 @@ class Economics:
|
|||
self.fee_rate = fee_rate
|
||||
|
||||
@property
|
||||
def pre_application_deployment_parameters(self) -> Tuple[int, ...]:
|
||||
def taco_application_deployment_parameters(self) -> Tuple[int, ...]:
|
||||
"""Cast coefficient attributes to uint256 compatible type for solidity+EVM"""
|
||||
deploy_parameters = ( # note: order-sensitive
|
||||
self.min_authorization,
|
||||
|
@ -103,10 +103,10 @@ class EconomicsFactory:
|
|||
|
||||
# Agents
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent, registry=registry, provider_uri=eth_provider_uri
|
||||
TACoApplicationAgent, registry=registry, provider_uri=eth_provider_uri
|
||||
)
|
||||
|
||||
# PRE Application
|
||||
# TACo Application
|
||||
min_authorization = application_agent.get_min_authorization()
|
||||
min_operator_seconds = application_agent.get_min_operator_seconds()
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
|
||||
|
@ -8,4 +6,5 @@ from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
|
|||
BASE_DIRECTORY = Path(__file__).parent
|
||||
CONTRACT_REGISTRY_BASE = BASE_DIRECTORY / "contract_registry"
|
||||
|
||||
|
||||
NO_BLOCKCHAIN_CONNECTION.bool_value(False)
|
||||
|
|
|
@ -33,7 +33,7 @@ from nucypher.blockchain.eth.agents import (
|
|||
ContractAgency,
|
||||
CoordinatorAgent,
|
||||
NucypherTokenAgent,
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.decorators import save_receipt, validate_checksum_address
|
||||
|
@ -204,7 +204,7 @@ class Operator(BaseActor):
|
|||
self.__staking_provider_address = None # set by block_until_ready
|
||||
if is_me:
|
||||
self.application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
provider_uri=eth_provider_uri,
|
||||
registry=self.registry,
|
||||
)
|
||||
|
@ -324,6 +324,18 @@ class Ritualist(BaseActor):
|
|||
condition_provider_uris
|
||||
)
|
||||
|
||||
self.set_provider_public_key()
|
||||
|
||||
def set_provider_public_key(self):
|
||||
# Here we're assuming there is one global key per node.
|
||||
is_provider_key_set = self.coordinator_agent.is_provider_public_key_set(
|
||||
self.staking_provider_address,
|
||||
)
|
||||
if not is_provider_key_set:
|
||||
self.coordinator_agent.set_provider_public_key(
|
||||
self.ritual_power.public_key(), transacting_power=self.transacting_power
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _is_permitted_condition_chain(chain_id: int) -> bool:
|
||||
return int(chain_id) in [int(cid) for cid in _CONDITION_CHAINS.keys()]
|
||||
|
@ -458,7 +470,7 @@ class Ritualist(BaseActor):
|
|||
def perform_round_1(
|
||||
self,
|
||||
ritual_id: int,
|
||||
initiator: ChecksumAddress,
|
||||
authority: ChecksumAddress,
|
||||
participants: List[ChecksumAddress],
|
||||
timestamp: int,
|
||||
) -> Optional[HexBytes]:
|
||||
|
@ -501,7 +513,9 @@ class Ritualist(BaseActor):
|
|||
)
|
||||
return None
|
||||
|
||||
self.log.debug(f"performing round 1 of DKG ritual #{ritual_id} from blocktime {timestamp}")
|
||||
self.log.debug(
|
||||
f"performing round 1 of DKG ritual #{ritual_id} from blocktime {timestamp} with authority {authority}."
|
||||
)
|
||||
|
||||
# gather the cohort
|
||||
ritual = self.coordinator_agent.get_ritual(ritual_id, with_participants=True)
|
||||
|
@ -516,7 +530,7 @@ class Ritualist(BaseActor):
|
|||
try:
|
||||
transcript = self.ritual_power.generate_transcript(
|
||||
nodes=nodes,
|
||||
threshold=(ritual.shares // 2) + 1, # TODO: #3095 This is a constant or needs to be stored somewhere else
|
||||
threshold=ritual.threshold,
|
||||
shares=ritual.shares,
|
||||
checksum_address=self.checksum_address,
|
||||
ritual_id=ritual_id
|
||||
|
@ -538,7 +552,7 @@ class Ritualist(BaseActor):
|
|||
arrival = ritual.total_transcripts + 1
|
||||
self.log.debug(
|
||||
f"{self.transacting_power.account[:8]} submitted a transcript for "
|
||||
f"DKG ritual #{ritual_id} ({arrival}/{len(ritual.providers)}) initiated by {initiator}"
|
||||
f"DKG ritual #{ritual_id} ({arrival}/{len(ritual.providers)}) with authority {authority}."
|
||||
)
|
||||
return tx_hash
|
||||
|
||||
|
@ -589,7 +603,7 @@ class Ritualist(BaseActor):
|
|||
# Aggregate the transcripts
|
||||
try:
|
||||
result = self.ritual_power.aggregate_transcripts(
|
||||
threshold=(ritual.shares // 2) + 1, # TODO: #3095 This is a constant or needs to be stored somewhere else
|
||||
threshold=ritual.threshold,
|
||||
shares=ritual.shares,
|
||||
checksum_address=self.checksum_address,
|
||||
ritual_id=ritual_id,
|
||||
|
@ -644,13 +658,12 @@ class Ritualist(BaseActor):
|
|||
f"ritual #{ritual_id} is missing transcripts"
|
||||
)
|
||||
|
||||
threshold = (ritual.shares // 2) + 1
|
||||
# TODO: consider the usage of local DKG artifact storage here #3052
|
||||
# aggregated_transcript_bytes = self.dkg_storage.get_aggregated_transcript(ritual_id)
|
||||
aggregated_transcript = AggregatedTranscript.from_bytes(bytes(ritual.aggregated_transcript))
|
||||
decryption_share = self.ritual_power.derive_decryption_share(
|
||||
nodes=nodes,
|
||||
threshold=threshold,
|
||||
threshold=ritual.threshold,
|
||||
shares=ritual.shares,
|
||||
checksum_address=self.checksum_address,
|
||||
ritual_id=ritual_id,
|
||||
|
@ -686,7 +699,7 @@ class PolicyAuthor(NucypherTokenActor):
|
|||
def __init__(self, eth_provider_uri: str, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent, registry=self.registry, provider_uri=eth_provider_uri
|
||||
TACoApplicationAgent, registry=self.registry, provider_uri=eth_provider_uri
|
||||
)
|
||||
|
||||
def create_policy(self, *args, **kwargs):
|
||||
|
|
|
@ -26,7 +26,12 @@ from eth_typing.evm import ChecksumAddress
|
|||
from eth_utils.address import to_checksum_address
|
||||
from hexbytes import HexBytes
|
||||
from nucypher_core import SessionStaticKey
|
||||
from nucypher_core.ferveo import AggregatedTranscript, DkgPublicKey, Transcript
|
||||
from nucypher_core.ferveo import (
|
||||
AggregatedTranscript,
|
||||
DkgPublicKey,
|
||||
FerveoPublicKey,
|
||||
Transcript,
|
||||
)
|
||||
from web3.contract.contract import Contract, ContractFunction
|
||||
from web3.types import Timestamp, TxParams, TxReceipt, Wei
|
||||
|
||||
|
@ -38,12 +43,14 @@ from nucypher.blockchain.eth.constants import (
|
|||
ETH_ADDRESS_BYTE_LENGTH,
|
||||
NUCYPHER_TOKEN_CONTRACT_NAME,
|
||||
NULL_ADDRESS,
|
||||
PRE_APPLICATION_CONTRACT_NAME,
|
||||
SUBSCRIPTION_MANAGER_CONTRACT_NAME,
|
||||
TACO_APPLICATION_CONTRACT_NAME,
|
||||
)
|
||||
from nucypher.blockchain.eth.decorators import contract_api
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.registry import BaseContractRegistry
|
||||
from nucypher.blockchain.eth.registry import (
|
||||
BaseContractRegistry,
|
||||
)
|
||||
from nucypher.config.constants import (
|
||||
NUCYPHER_ENVVAR_STAKING_PROVIDERS_PAGINATION_SIZE,
|
||||
NUCYPHER_ENVVAR_STAKING_PROVIDERS_PAGINATION_SIZE_LIGHT_NODE,
|
||||
|
@ -382,9 +389,9 @@ class AdjudicatorAgent(EthereumContractAgent):
|
|||
return staking_parameters
|
||||
|
||||
|
||||
class PREApplicationAgent(EthereumContractAgent):
|
||||
class TACoApplicationAgent(EthereumContractAgent):
|
||||
|
||||
contract_name: str = PRE_APPLICATION_CONTRACT_NAME
|
||||
contract_name: str = TACO_APPLICATION_CONTRACT_NAME
|
||||
|
||||
DEFAULT_PROVIDERS_PAGINATION_SIZE_LIGHT_NODE = int(os.environ.get(NUCYPHER_ENVVAR_STAKING_PROVIDERS_PAGINATION_SIZE_LIGHT_NODE, default=30))
|
||||
DEFAULT_PROVIDERS_PAGINATION_SIZE = int(os.environ.get(NUCYPHER_ENVVAR_STAKING_PROVIDERS_PAGINATION_SIZE, default=1000))
|
||||
|
@ -399,7 +406,7 @@ class PREApplicationAgent(EthereumContractAgent):
|
|||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
def get_min_authorization(self) -> int:
|
||||
result = self.contract.functions.minAuthorization().call()
|
||||
result = self.contract.functions.minimumAuthorization().call()
|
||||
return result
|
||||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
|
@ -574,6 +581,38 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
contract_name: str = "Coordinator"
|
||||
_proxy_name = None
|
||||
|
||||
class G2Point(NamedTuple):
|
||||
"""
|
||||
Coordinator contract representation of Ferveo Participant public key.
|
||||
"""
|
||||
|
||||
# TODO validation of these if used directly
|
||||
word0: bytes # 32 bytes
|
||||
word1: bytes # 32 bytes
|
||||
word2: bytes # 32 bytes
|
||||
|
||||
@classmethod
|
||||
def from_public_key(cls, public_key: FerveoPublicKey):
|
||||
return cls.from_bytes(bytes(public_key))
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes):
|
||||
if len(data) != FerveoPublicKey.serialized_size():
|
||||
raise ValueError(
|
||||
f"Invalid byte length; expected {FerveoPublicKey.serialized_size()} bytes but got {len(data)} bytes for G2Point"
|
||||
)
|
||||
return cls(word0=data[:32], word1=data[32:64], word2=data[64:96])
|
||||
|
||||
def to_public_key(self) -> FerveoPublicKey:
|
||||
data = bytes(self)
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return FerveoPublicKey.from_bytes(data)
|
||||
|
||||
def __bytes__(self):
|
||||
return self.word0 + self.word1 + self.word2
|
||||
|
||||
@dataclass
|
||||
class Ritual:
|
||||
|
||||
|
@ -623,8 +662,13 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
return self.word0 + self.word1
|
||||
|
||||
initiator: ChecksumAddress
|
||||
authority: ChecksumAddress
|
||||
access_controller: ChecksumAddress
|
||||
dkg_size: int
|
||||
init_timestamp: int
|
||||
end_timestamp: int
|
||||
threshold: int
|
||||
|
||||
total_transcripts: int = 0
|
||||
total_aggregations: int = 0
|
||||
public_key: G1Point = None
|
||||
|
@ -666,17 +710,21 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
result = self.contract.functions.rituals(int(ritual_id)).call()
|
||||
ritual = self.Ritual(
|
||||
initiator=ChecksumAddress(result[0]),
|
||||
dkg_size=result[1],
|
||||
init_timestamp=result[2],
|
||||
init_timestamp=result[1],
|
||||
end_timestamp=result[2],
|
||||
total_transcripts=result[3],
|
||||
total_aggregations=result[4],
|
||||
aggregation_mismatch=result[6],
|
||||
aggregated_transcript=bytes(result[7]),
|
||||
authority=ChecksumAddress(result[5]),
|
||||
dkg_size=result[6],
|
||||
threshold=result[7],
|
||||
aggregation_mismatch=result[8],
|
||||
access_controller=ChecksumAddress(result[9]),
|
||||
aggregated_transcript=bytes(result[11]),
|
||||
participants=[], # solidity does not return sub-structs
|
||||
)
|
||||
|
||||
# public key
|
||||
ritual.public_key = self.Ritual.G1Point(result[5][0], result[5][1])
|
||||
ritual.public_key = self.Ritual.G1Point(result[10][0], result[10][1])
|
||||
|
||||
# participants
|
||||
if with_participants:
|
||||
|
@ -703,6 +751,16 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
participants.append(participant)
|
||||
return participants
|
||||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
def get_provider_public_key(
|
||||
self, provider: ChecksumAddress, ritual_id: int
|
||||
) -> FerveoPublicKey:
|
||||
result = self.contract.functions.getProviderPublicKey(
|
||||
provider, ritual_id
|
||||
).call()
|
||||
g2_point = self.G2Point(result[0], result[1], result[2])
|
||||
return g2_point.to_public_key()
|
||||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
def number_of_rituals(self) -> int:
|
||||
result = self.contract.functions.numberOfRituals().call()
|
||||
|
@ -723,12 +781,47 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
)
|
||||
return participant
|
||||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
def is_encryption_authorized(
|
||||
self, ritual_id: int, evidence: bytes, ciphertext_header: bytes
|
||||
) -> bool:
|
||||
"""
|
||||
This contract read is relayed through coordinator to the access controller
|
||||
contract associated with a given ritual.
|
||||
"""
|
||||
result = self.contract.functions.isEncryptionAuthorized(
|
||||
ritual_id, evidence, ciphertext_header
|
||||
).call()
|
||||
return result
|
||||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
def is_provider_public_key_set(self, staking_provider: ChecksumAddress) -> bool:
|
||||
result = self.contract.functions.isProviderPublicKeySet(staking_provider).call()
|
||||
return result
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def set_provider_public_key(
|
||||
self, public_key: FerveoPublicKey, transacting_power: TransactingPower
|
||||
) -> TxReceipt:
|
||||
contract_function = self.contract.functions.setProviderPublicKey(
|
||||
self.G2Point.from_public_key(public_key)
|
||||
)
|
||||
receipt = self.blockchain.send_transaction(
|
||||
contract_function=contract_function, transacting_power=transacting_power
|
||||
)
|
||||
return receipt
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def initiate_ritual(
|
||||
self, providers: List[ChecksumAddress], transacting_power: TransactingPower
|
||||
self,
|
||||
providers: List[ChecksumAddress],
|
||||
authority: ChecksumAddress,
|
||||
duration: int,
|
||||
access_controller: ChecksumAddress,
|
||||
transacting_power: TransactingPower,
|
||||
) -> TxReceipt:
|
||||
contract_function: ContractFunction = self.contract.functions.initiateRitual(
|
||||
providers
|
||||
providers, authority, duration, access_controller
|
||||
)
|
||||
receipt = self.blockchain.send_transaction(
|
||||
contract_function=contract_function, transacting_power=transacting_power
|
||||
|
@ -766,7 +859,7 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
contract_function: ContractFunction = self.contract.functions.postAggregation(
|
||||
ritualId=ritual_id,
|
||||
aggregatedTranscript=bytes(aggregated_transcript),
|
||||
publicKey=self.Ritual.G1Point.from_dkg_public_key(public_key),
|
||||
dkgPublicKey=self.Ritual.G1Point.from_dkg_public_key(public_key),
|
||||
decryptionRequestStaticKey=bytes(participant_public_key),
|
||||
)
|
||||
receipt = self.blockchain.send_transaction(
|
||||
|
@ -776,6 +869,21 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
)
|
||||
return receipt
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def get_ritual_initiation_cost(
|
||||
self, providers: List[ChecksumAddress], duration: int
|
||||
) -> Wei:
|
||||
result = self.contract.functions.getRitualInitiationCost(
|
||||
providers, duration
|
||||
).call()
|
||||
return Wei(result)
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def get_ritual_id_from_public_key(self, public_key: DkgPublicKey) -> int:
|
||||
g1_point = self.Ritual.G1Point.from_dkg_public_key(public_key)
|
||||
result = self.contract.functions.getRitualIdFromPublicKey(g1_point).call()
|
||||
return result
|
||||
|
||||
def get_ritual_public_key(self, ritual_id: int) -> DkgPublicKey:
|
||||
if self.get_ritual_status(ritual_id=ritual_id) != self.Ritual.Status.FINALIZED:
|
||||
# TODO should we raise here instead?
|
||||
|
@ -787,13 +895,6 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
|
||||
return ritual.public_key.to_dkg_public_key()
|
||||
|
||||
def is_encryption_authorized(
|
||||
self, ritual_id: int, evidence: bytes, digest: bytes
|
||||
) -> bool:
|
||||
# TODO: actually call contract.
|
||||
# get ritual -> get access controller -> call isAuthorized(ritualId, evidence, digest)
|
||||
return True
|
||||
|
||||
|
||||
class ContractAgency:
|
||||
"""Where agents live and die."""
|
||||
|
@ -843,8 +944,6 @@ class ContractAgency:
|
|||
if name == NUCYPHER_TOKEN_CONTRACT_NAME:
|
||||
# TODO: Perhaps rename NucypherTokenAgent
|
||||
name = "NucypherToken"
|
||||
if name == PRE_APPLICATION_CONTRACT_NAME:
|
||||
name = "PREApplication" # TODO not needed once full PRE Application is used
|
||||
agent_name = f"{name}Agent"
|
||||
return agent_name
|
||||
|
||||
|
@ -930,7 +1029,9 @@ class StakingProvidersReservoir:
|
|||
|
||||
def draw(self, quantity):
|
||||
if quantity > len(self):
|
||||
raise PREApplicationAgent.NotEnoughStakingProviders(f'Cannot sample {quantity} out of {len(self)} total staking providers')
|
||||
raise TACoApplicationAgent.NotEnoughStakingProviders(
|
||||
f"Cannot sample {quantity} out of {len(self)} total staking providers"
|
||||
)
|
||||
|
||||
return self._sampler.sample_no_replacement(self._rng, quantity)
|
||||
|
||||
|
|
|
@ -4,20 +4,20 @@
|
|||
# Contract Names
|
||||
#
|
||||
|
||||
DISPATCHER_CONTRACT_NAME = 'Dispatcher'
|
||||
NUCYPHER_TOKEN_CONTRACT_NAME = 'NuCypherToken'
|
||||
STAKING_ESCROW_CONTRACT_NAME = 'StakingEscrow'
|
||||
STAKING_ESCROW_STUB_CONTRACT_NAME = 'StakingEscrowStub'
|
||||
ADJUDICATOR_CONTRACT_NAME = 'Adjudicator'
|
||||
PRE_APPLICATION_CONTRACT_NAME = 'SimplePREApplication' # TODO: Use the real PREApplication
|
||||
SUBSCRIPTION_MANAGER_CONTRACT_NAME = 'SubscriptionManager'
|
||||
DISPATCHER_CONTRACT_NAME = "Dispatcher"
|
||||
NUCYPHER_TOKEN_CONTRACT_NAME = "NuCypherToken"
|
||||
STAKING_ESCROW_CONTRACT_NAME = "StakingEscrow"
|
||||
STAKING_ESCROW_STUB_CONTRACT_NAME = "StakingEscrowStub"
|
||||
ADJUDICATOR_CONTRACT_NAME = "Adjudicator"
|
||||
TACO_APPLICATION_CONTRACT_NAME = "TACoApplication"
|
||||
SUBSCRIPTION_MANAGER_CONTRACT_NAME = "SubscriptionManager"
|
||||
|
||||
NUCYPHER_CONTRACT_NAMES = (
|
||||
NUCYPHER_TOKEN_CONTRACT_NAME,
|
||||
STAKING_ESCROW_CONTRACT_NAME,
|
||||
ADJUDICATOR_CONTRACT_NAME,
|
||||
DISPATCHER_CONTRACT_NAME,
|
||||
PRE_APPLICATION_CONTRACT_NAME,
|
||||
TACO_APPLICATION_CONTRACT_NAME,
|
||||
SUBSCRIPTION_MANAGER_CONTRACT_NAME
|
||||
)
|
||||
|
||||
|
|
|
@ -259,6 +259,9 @@ class _UpAndDownInTheWater(Bob, DKGOmniscient):
|
|||
def __init__(self, session_seed=None, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def get_ritual_id_from_public_key(self, public_key) -> int:
|
||||
return 55 # any ritual id can be returned here
|
||||
|
||||
def get_ritual_from_id(self, ritual_id):
|
||||
return self._dkg_insight.fake_ritual
|
||||
|
||||
|
|
|
@ -74,13 +74,14 @@ from nucypher.blockchain.eth import actors
|
|||
from nucypher.blockchain.eth.agents import (
|
||||
ContractAgency,
|
||||
CoordinatorAgent,
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.registry import (
|
||||
BaseContractRegistry,
|
||||
InMemoryContractRegistry,
|
||||
)
|
||||
from nucypher.blockchain.eth.signers import Signer
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.characters.banners import (
|
||||
ALICE_BANNER,
|
||||
|
@ -677,7 +678,9 @@ class Bob(Character):
|
|||
)
|
||||
|
||||
if len(successes) < threshold:
|
||||
raise Ursula.NotEnoughUrsulas(f"Not enough Ursulas to decrypt: {failures}")
|
||||
raise Ursula.NotEnoughUrsulas(
|
||||
f"Threshold of Ursulas unable to decrypt: {failures}"
|
||||
)
|
||||
self.log.debug("Got enough shares to decrypt.")
|
||||
|
||||
if decryption_request.variant == FerveoVariant.Precomputed:
|
||||
|
@ -701,25 +704,37 @@ class Bob(Character):
|
|||
gathered_shares[provider_address] = decryption_share
|
||||
return gathered_shares
|
||||
|
||||
def get_ritual_from_id(self, ritual_id):
|
||||
# blockchain reads: get the DKG parameters and the cohort.
|
||||
def _get_coordinator_agent(self) -> CoordinatorAgent:
|
||||
if not self.coordinator_agent:
|
||||
raise ValueError(
|
||||
"No coordinator provider URI provided in Bob's constructor."
|
||||
)
|
||||
ritual = self.coordinator_agent.get_ritual(ritual_id, with_participants=True)
|
||||
|
||||
return self.coordinator_agent
|
||||
|
||||
def get_ritual_id_from_public_key(self, public_key: DkgPublicKey) -> int:
|
||||
ritual_id = self._get_coordinator_agent().get_ritual_id_from_public_key(
|
||||
public_key
|
||||
)
|
||||
return ritual_id
|
||||
|
||||
def get_ritual_from_id(self, ritual_id) -> CoordinatorAgent.Ritual:
|
||||
ritual = self._get_coordinator_agent().get_ritual(
|
||||
ritual_id, with_participants=True
|
||||
)
|
||||
return ritual
|
||||
|
||||
def threshold_decrypt(
|
||||
self,
|
||||
ritual_id: int,
|
||||
threshold_message_kit: ThresholdMessageKit,
|
||||
context: Optional[dict] = None,
|
||||
ursulas: Optional[List["Ursula"]] = None,
|
||||
peering_timeout: int = 60,
|
||||
) -> bytes:
|
||||
ritual = self.get_ritual_from_id(ritual_id)
|
||||
ritual_id = self.get_ritual_id_from_public_key(
|
||||
public_key=threshold_message_kit.acp.public_key
|
||||
)
|
||||
ritual = self.get_ritual_from_id(ritual_id=ritual_id)
|
||||
|
||||
if not ursulas:
|
||||
# P2P: if the Ursulas are not provided, we need to resolve them from published records.
|
||||
|
@ -735,11 +750,6 @@ class Bob(Character):
|
|||
self.remember_node(ursula)
|
||||
|
||||
variant = self._default_dkg_variant
|
||||
threshold = (
|
||||
(ritual.shares // 2) + 1
|
||||
if variant == FerveoVariant.Simple
|
||||
else ritual.shares
|
||||
) # TODO: #3095 get this from the ritual / put it on-chain?
|
||||
|
||||
decryption_request = self.__make_decryption_request(
|
||||
ritual_id=ritual_id,
|
||||
|
@ -752,7 +762,7 @@ class Bob(Character):
|
|||
decryption_request=decryption_request,
|
||||
participant_public_keys=participant_public_keys,
|
||||
cohort=ursulas,
|
||||
threshold=threshold,
|
||||
threshold=ritual.threshold,
|
||||
)
|
||||
|
||||
return self.__decrypt(
|
||||
|
@ -1305,7 +1315,7 @@ class Ursula(Teacher, Character, actors.Operator, actors.Ritualist):
|
|||
# Check the node's stake (optional)
|
||||
if minimum_stake > 0 and staking_provider_address:
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent, provider_uri=provider_uri, registry=registry
|
||||
TACoApplicationAgent, provider_uri=provider_uri, registry=registry
|
||||
)
|
||||
seednode_stake = application_agent.get_authorized_stake(
|
||||
staking_provider=staking_provider_address
|
||||
|
@ -1450,9 +1460,13 @@ class Enrico:
|
|||
|
||||
banner = ENRICO_BANNER
|
||||
|
||||
def __init__(self, encrypting_key: Union[PublicKey, DkgPublicKey]):
|
||||
self.signing_power = SigningPower()
|
||||
self._policy_pubkey = encrypting_key
|
||||
def __init__(
|
||||
self,
|
||||
encrypting_key: Union[PublicKey, DkgPublicKey],
|
||||
signer: Optional[Signer] = None,
|
||||
):
|
||||
self.signer = signer
|
||||
self._encrypting_key = encrypting_key
|
||||
self.log = Logger(f"{self.__class__.__name__}-{encrypting_key}")
|
||||
self.log.info(self.banner.format(encrypting_key))
|
||||
|
||||
|
@ -1470,20 +1484,29 @@ class Enrico:
|
|||
return message_kit
|
||||
|
||||
def encrypt_for_dkg(
|
||||
self, plaintext: bytes, conditions: Lingo
|
||||
self,
|
||||
plaintext: bytes,
|
||||
conditions: Lingo,
|
||||
) -> ThresholdMessageKit:
|
||||
if not self.signer:
|
||||
raise TypeError("This Enrico doesn't have a signer.")
|
||||
|
||||
validate_condition_lingo(conditions)
|
||||
conditions_json = json.dumps(conditions)
|
||||
access_conditions = Conditions(conditions_json)
|
||||
|
||||
# encrypt for DKG
|
||||
ciphertext, auth_data = encrypt_for_dkg(
|
||||
plaintext, self.policy_pubkey, access_conditions
|
||||
)
|
||||
|
||||
# authentication for AllowLogic
|
||||
# TODO Replace with `Signer` to be passed as parameter
|
||||
# authentication message for TACo
|
||||
header_hash = keccak_digest(bytes(ciphertext.header))
|
||||
authorization = self.signing_power.keypair.sign(header_hash).to_be_bytes()
|
||||
authorization = bytes(
|
||||
self.signer.sign_message(
|
||||
message=header_hash, account=self.signer.accounts[0]
|
||||
)
|
||||
)
|
||||
|
||||
return ThresholdMessageKit(
|
||||
ciphertext=ciphertext,
|
||||
|
@ -1502,8 +1525,8 @@ class Enrico:
|
|||
|
||||
@property
|
||||
def policy_pubkey(self):
|
||||
if not self._policy_pubkey:
|
||||
if not self._encrypting_key:
|
||||
raise TypeError(
|
||||
"This Enrico doesn't know which policy encrypting key he used. Oh well."
|
||||
)
|
||||
return self._policy_pubkey
|
||||
return self._encrypting_key
|
||||
|
|
|
@ -11,7 +11,6 @@ from nucypher.config.constants import TEMPORARY_DOMAIN
|
|||
from nucypher.crypto.powers import CryptoPower
|
||||
from nucypher.exceptions import DevelopmentInstallationRequired
|
||||
from nucypher.policy.payment import FreeReencryptions
|
||||
from tests.constants import TEST_ETH_PROVIDER_URI, TESTERCHAIN_CHAIN_ID
|
||||
|
||||
|
||||
class Vladimir(Ursula):
|
||||
|
@ -40,12 +39,16 @@ class Vladimir(Ursula):
|
|||
try:
|
||||
from tests.utils.middleware import EvilMiddleWare
|
||||
except ImportError:
|
||||
raise DevelopmentInstallationRequired(importable_name='tests.utils.middleware.EvilMiddleWare')
|
||||
cls.network_middleware = EvilMiddleWare(eth_provider_uri=TEST_ETH_PROVIDER_URI)
|
||||
raise DevelopmentInstallationRequired(
|
||||
importable_name="tests.utils.middleware.EvilMiddleWare"
|
||||
)
|
||||
blockchain = target_ursula.application_agent.blockchain
|
||||
cls.network_middleware = EvilMiddleWare(
|
||||
eth_provider_uri=blockchain.eth_provider_uri
|
||||
)
|
||||
|
||||
crypto_power = CryptoPower(power_ups=target_ursula._default_crypto_powerups)
|
||||
|
||||
blockchain = target_ursula.application_agent.blockchain
|
||||
cls.attach_transacting_key(blockchain=blockchain)
|
||||
|
||||
# Vladimir does not care about payment.
|
||||
|
@ -53,10 +56,12 @@ class Vladimir(Ursula):
|
|||
bogus_payment_method.provider = Mock()
|
||||
bogus_payment_method.agent = Mock()
|
||||
bogus_payment_method.network = TEMPORARY_DOMAIN
|
||||
bogus_payment_method.agent.blockchain.client.chain_id = TESTERCHAIN_CHAIN_ID
|
||||
bogus_payment_method.agent.blockchain.client.chain_id = (
|
||||
blockchain.client.chain_id
|
||||
)
|
||||
mock.patch(
|
||||
"mock.interfaces.MockBlockchain.client.chain_id",
|
||||
new_callable=mock.PropertyMock(return_value=TESTERCHAIN_CHAIN_ID),
|
||||
new_callable=mock.PropertyMock(return_value=blockchain.client.chain_id),
|
||||
)
|
||||
|
||||
vladimir = cls(is_me=True,
|
||||
|
|
|
@ -10,7 +10,7 @@ from nucypher.blockchain.eth.agents import (
|
|||
ContractAgency,
|
||||
EthereumContractAgent,
|
||||
NucypherTokenAgent,
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.constants import AVERAGE_BLOCK_TIME_IN_SECONDS
|
||||
from nucypher.blockchain.eth.networks import NetworksInventory
|
||||
|
@ -41,7 +41,7 @@ STAKING_ESCROW = 'StakingEscrow'
|
|||
POLICY_MANAGER = 'PolicyManager'
|
||||
|
||||
CONTRACT_NAMES = [
|
||||
PREApplicationAgent.contract_name,
|
||||
TACoApplicationAgent.contract_name,
|
||||
NucypherTokenAgent.contract_name,
|
||||
STAKING_ESCROW,
|
||||
POLICY_MANAGER
|
||||
|
@ -120,15 +120,23 @@ def network(general_config, registry_options):
|
|||
paint_contract_status(registry, emitter=emitter)
|
||||
|
||||
|
||||
@status.command('pre')
|
||||
@status.command("taco")
|
||||
@group_registry_options
|
||||
@option_staking_provider
|
||||
@group_general_config
|
||||
def staking_providers(general_config, registry_options, staking_provider_address):
|
||||
"""Show relevant information about staking providers."""
|
||||
emitter, registry, blockchain = registry_options.setup(general_config=general_config)
|
||||
application_agent = ContractAgency.get_agent(PREApplicationAgent, registry=registry)
|
||||
staking_providers_list = [staking_provider_address] if staking_provider_address else application_agent.get_staking_providers()
|
||||
emitter, registry, blockchain = registry_options.setup(
|
||||
general_config=general_config
|
||||
)
|
||||
application_agent = ContractAgency.get_agent(
|
||||
TACoApplicationAgent, registry=registry
|
||||
)
|
||||
staking_providers_list = (
|
||||
[staking_provider_address]
|
||||
if staking_provider_address
|
||||
else application_agent.get_staking_providers()
|
||||
)
|
||||
emitter.echo(staking_providers_list) # TODO: staking provider painter
|
||||
# paint_stakers(emitter=emitter, stakers=staking_providers_list, registry=registry)
|
||||
|
||||
|
@ -164,8 +172,8 @@ def events(general_config, registry_options, contract_name, from_block, to_block
|
|||
if event_name:
|
||||
raise click.BadOptionUsage(option_name='--event-name', message=click.style('--event-name requires --contract-name', fg="red"))
|
||||
# FIXME should we force a contract name to be specified?
|
||||
# default to PREApplication contract
|
||||
contract_names = [PREApplicationAgent.contract_name]
|
||||
# default to TACoApplication contract
|
||||
contract_names = [TACoApplicationAgent.contract_name]
|
||||
else:
|
||||
contract_names = [contract_name]
|
||||
|
||||
|
|
|
@ -177,7 +177,6 @@ WARNING: --etherscan is disabled. If you want to see deployed contracts and TXs
|
|||
"""
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Ursula
|
||||
#
|
||||
|
@ -186,7 +185,7 @@ SUCCESSFUL_MANUALLY_SAVE_METADATA = "Successfully saved node metadata to {metada
|
|||
|
||||
|
||||
#
|
||||
# PREApplication
|
||||
# TACoApplication
|
||||
#
|
||||
|
||||
STAKING_PROVIDER_UNAUTHORIZED = '{provider} is not authorized.'
|
||||
|
|
|
@ -4,14 +4,16 @@ from web3.main import Web3
|
|||
|
||||
from nucypher.blockchain.eth.agents import (
|
||||
ContractAgency,
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
|
||||
|
||||
def paint_contract_status(registry, emitter):
|
||||
blockchain = BlockchainInterfaceFactory.get_interface()
|
||||
application_agent = ContractAgency.get_agent(PREApplicationAgent, registry=registry)
|
||||
application_agent = ContractAgency.get_agent(
|
||||
TACoApplicationAgent, registry=registry
|
||||
)
|
||||
contracts = f"""
|
||||
| Contract Deployments |
|
||||
{application_agent.contract_name} .............. {application_agent.contract_address}
|
||||
|
@ -25,7 +27,7 @@ Registry ................. {registry.filepath}
|
|||
"""
|
||||
|
||||
staking = f"""
|
||||
| PREApplication |
|
||||
| TACoApplication |
|
||||
Staking Provider Population ....... {application_agent.get_staking_providers_population()}
|
||||
"""
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ from twisted.internet.defer import Deferred
|
|||
from nucypher import characters
|
||||
from nucypher.acumen.nicknames import Nickname
|
||||
from nucypher.acumen.perception import FleetSensor
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.networks import NetworksInventory
|
||||
from nucypher.blockchain.eth.registry import BaseContractRegistry
|
||||
|
@ -1048,8 +1048,8 @@ class Teacher:
|
|||
the case that the "staking provider" isn't "staking" (e.g., all her tokens have been slashed).
|
||||
"""
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent, provider_uri=provider_uri, registry=registry
|
||||
) # type: PREApplicationAgent
|
||||
TACoApplicationAgent, provider_uri=provider_uri, registry=registry
|
||||
) # type: TACoApplicationAgent
|
||||
staking_provider_address = application_agent.get_staking_provider_from_operator(operator_address=self.operator_address)
|
||||
if staking_provider_address == NULL_ADDRESS:
|
||||
raise self.UnbondedOperator(f"Operator {self.operator_address} is not bonded")
|
||||
|
@ -1061,8 +1061,8 @@ class Teacher:
|
|||
As a follow-up, this checks that the staking provider is, indeed, staking.
|
||||
"""
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent, registry=registry, provider_uri=eth_provider_uri
|
||||
) # type: PREApplicationAgent
|
||||
TACoApplicationAgent, registry=registry, provider_uri=eth_provider_uri
|
||||
) # type: TACoApplicationAgent
|
||||
is_staking = application_agent.is_authorized(staking_provider=self.checksum_address) # checksum address here is staking provider
|
||||
return is_staking
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ from nucypher_core import (
|
|||
from nucypher.config.constants import MAX_UPLOAD_CONTENT_LENGTH
|
||||
from nucypher.crypto.keypairs import DecryptingKeypair
|
||||
from nucypher.crypto.signing import InvalidSignature
|
||||
from nucypher.crypto.utils import keccak_digest
|
||||
from nucypher.network.exceptions import NodeSeemsToBeDown
|
||||
from nucypher.network.nodes import NodeSprout
|
||||
from nucypher.network.protocols import InterfaceInfo
|
||||
|
@ -166,13 +165,12 @@ def _make_rest_app(this_node, log: Logger) -> Flask:
|
|||
|
||||
ciphertext_header = decryption_request.ciphertext_header
|
||||
|
||||
# check whether enrico is authorized - AllowLogic
|
||||
# check whether enrico is authorized
|
||||
authorization = decryption_request.acp.authorization
|
||||
ciphertext_header_hash = keccak_digest(bytes(ciphertext_header))
|
||||
if not this_node.coordinator_agent.is_encryption_authorized(
|
||||
ritual_id=decryption_request.ritual_id,
|
||||
evidence=authorization,
|
||||
digest=ciphertext_header_hash,
|
||||
ciphertext_header=bytes(ciphertext_header),
|
||||
):
|
||||
return Response(
|
||||
f"Encrypted data not authorized for ritual {decryption_request.ritual_id}",
|
||||
|
|
|
@ -9,7 +9,7 @@ from twisted.internet.task import LoopingCall
|
|||
from twisted.python.failure import Failure
|
||||
|
||||
from nucypher import characters
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.network.exceptions import NodeSeemsToBeDown
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
|
@ -31,7 +31,7 @@ class OperatorBondedTracker(SimpleTask):
|
|||
|
||||
def run(self) -> None:
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=self._ursula.registry,
|
||||
provider_uri=self._ursula.eth_provider_uri,
|
||||
)
|
||||
|
|
|
@ -3,13 +3,13 @@ from typing import Iterable, List, Optional
|
|||
from eth_typing import ChecksumAddress
|
||||
|
||||
from nucypher.blockchain.eth.agents import (
|
||||
PREApplicationAgent,
|
||||
StakingProvidersReservoir,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
|
||||
|
||||
def make_staking_provider_reservoir(
|
||||
application_agent: PREApplicationAgent,
|
||||
application_agent: TACoApplicationAgent,
|
||||
exclude_addresses: Optional[Iterable[ChecksumAddress]] = None,
|
||||
include_addresses: Optional[Iterable[ChecksumAddress]] = None,
|
||||
pagination_size: Optional[int] = None,
|
||||
|
@ -22,7 +22,7 @@ def make_staking_provider_reservoir(
|
|||
without_set = set(include_addresses) | set(exclude_addresses or ())
|
||||
try:
|
||||
reservoir = application_agent.get_staking_provider_reservoir(without=without_set, pagination_size=pagination_size)
|
||||
except PREApplicationAgent.NotEnoughStakingProviders:
|
||||
except TACoApplicationAgent.NotEnoughStakingProviders:
|
||||
# TODO: do that in `get_staking_provider_reservoir()`?
|
||||
reservoir = StakingProvidersReservoir({})
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ from nucypher.blockchain.eth import actors
|
|||
from nucypher.blockchain.eth.agents import (
|
||||
ContractAgency,
|
||||
EthereumContractAgent,
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.registry import BaseContractRegistry
|
||||
|
@ -178,7 +178,7 @@ class StakingProviderMetricsCollector(BaseMetricsCollector):
|
|||
|
||||
def _collect_internal(self) -> None:
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=self.contract_registry,
|
||||
provider_uri=self.eth_provider_uri,
|
||||
)
|
||||
|
|
|
@ -23,10 +23,11 @@ import sys
|
|||
|
||||
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, NucypherTokenAgent
|
||||
from nucypher.blockchain.eth.agents import (
|
||||
PREApplicationAgent,
|
||||
SubscriptionManagerAgent
|
||||
ContractAgency,
|
||||
NucypherTokenAgent,
|
||||
SubscriptionManagerAgent,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
|
||||
|
@ -62,9 +63,15 @@ blockchain.connect()
|
|||
registry = InMemoryContractRegistry.from_latest_publication(network=network)
|
||||
emitter.echo(f"NOTICE: Connecting to {network} network", color='yellow')
|
||||
|
||||
token_agent = ContractAgency.get_agent(agent_class=NucypherTokenAgent, registry=registry) # type: NucypherTokenAgent
|
||||
application_agent = ContractAgency.get_agent(agent_class=PREApplicationAgent, registry=registry) # type: PREApplicationAgent
|
||||
subscription_agent = ContractAgency.get_agent(agent_class=SubscriptionManagerAgent, registry=registry) # type: SubscriptionManagerAgent
|
||||
token_agent = ContractAgency.get_agent(
|
||||
agent_class=NucypherTokenAgent, registry=registry
|
||||
) # type: NucypherTokenAgent
|
||||
application_agent = ContractAgency.get_agent(
|
||||
agent_class=TACoApplicationAgent, registry=registry
|
||||
) # type: TACoApplicationAgent
|
||||
subscription_agent = ContractAgency.get_agent(
|
||||
agent_class=SubscriptionManagerAgent, registry=registry
|
||||
) # type: SubscriptionManagerAgent
|
||||
|
||||
message = f"NuCypher agents pre-loaded in variables 'token_agent', 'subscription_agent' and 'application_agent'"
|
||||
message = "NuCypher agents pre-loaded in variables 'token_agent', 'subscription_agent' and 'application_agent'"
|
||||
emitter.echo(message=message, color='green')
|
||||
|
|
|
@ -9,7 +9,7 @@ from web3 import Web3
|
|||
from nucypher.blockchain.eth.agents import (
|
||||
ContractAgency,
|
||||
CoordinatorAgent,
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
|
||||
from nucypher.blockchain.eth.signers import Signer
|
||||
|
@ -145,10 +145,10 @@ def nucypher_dkg(
|
|||
network=eth_staking_network
|
||||
)
|
||||
application_agent = ContractAgency.get_agent(
|
||||
agent_class=PREApplicationAgent,
|
||||
agent_class=TACoApplicationAgent,
|
||||
registry=staking_network_registry,
|
||||
provider_uri=eth_provider_uri,
|
||||
) # type: PREApplicationAgent
|
||||
) # type: TACoApplicationAgent
|
||||
|
||||
#
|
||||
# Initial Ritual
|
||||
|
@ -313,7 +313,6 @@ def nucypher_dkg(
|
|||
bob.start_learning_loop(now=True)
|
||||
|
||||
cleartext = bob.threshold_decrypt(
|
||||
ritual_id=ritual_id,
|
||||
threshold_message_kit=threshold_message_kit,
|
||||
)
|
||||
|
||||
|
|
|
@ -2,12 +2,11 @@ import pytest
|
|||
import pytest_twisted
|
||||
from twisted.internet.threads import deferToThread
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, CoordinatorAgent
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.blockchain.eth.trackers.dkg import EventScannerTask
|
||||
from nucypher.characters.lawful import Enrico
|
||||
from nucypher.characters.lawful import Enrico, Ursula
|
||||
from nucypher.policy.conditions.lingo import ConditionLingo
|
||||
from tests.acceptance.constants import APE_TEST_CHAIN_ID
|
||||
from tests.constants import TEST_ETH_PROVIDER_URI
|
||||
from tests.constants import APE_TEST_CHAIN_ID
|
||||
|
||||
# constants
|
||||
DKG_SIZE = 4
|
||||
|
@ -29,6 +28,8 @@ CONDITIONS = {
|
|||
},
|
||||
}
|
||||
|
||||
DURATION = 48 * 60 * 60
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def cohort(ursulas):
|
||||
|
@ -38,26 +39,40 @@ def cohort(ursulas):
|
|||
return nodes
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def coordinator_agent(testerchain, test_registry):
|
||||
"""Creates a coordinator agent"""
|
||||
return ContractAgency.get_agent(
|
||||
CoordinatorAgent, registry=test_registry, provider_uri=TEST_ETH_PROVIDER_URI
|
||||
)
|
||||
|
||||
|
||||
@pytest_twisted.inlineCallbacks()
|
||||
def test_ursula_ritualist(testerchain, coordinator_agent, cohort, alice, bob):
|
||||
def test_ursula_ritualist(
|
||||
testerchain,
|
||||
coordinator_agent,
|
||||
global_allow_list,
|
||||
cohort,
|
||||
initiator,
|
||||
bob,
|
||||
ritual_token,
|
||||
):
|
||||
"""Tests the DKG and the encryption/decryption of a message"""
|
||||
signer = Web3Signer(client=testerchain.client)
|
||||
|
||||
# Round 0 - Initiate the ritual
|
||||
def initialize():
|
||||
"""Initiates the ritual"""
|
||||
print("==================== INITIALIZING ====================")
|
||||
cohort_staking_provider_addresses = list(u.checksum_address for u in cohort)
|
||||
|
||||
# Approve the ritual token for the coordinator agent to spend
|
||||
amount = coordinator_agent.get_ritual_initiation_cost(
|
||||
providers=cohort_staking_provider_addresses, duration=DURATION
|
||||
)
|
||||
tx = ritual_token.functions.approve(
|
||||
coordinator_agent.contract_address, amount
|
||||
).transact({"from": initiator.transacting_power.account})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
receipt = coordinator_agent.initiate_ritual(
|
||||
providers=cohort_staking_provider_addresses,
|
||||
transacting_power=alice.transacting_power
|
||||
authority=initiator.transacting_power.account,
|
||||
duration=DURATION,
|
||||
access_controller=global_allow_list.address,
|
||||
transacting_power=initiator.transacting_power,
|
||||
)
|
||||
return receipt
|
||||
|
||||
|
@ -121,21 +136,42 @@ def test_ursula_ritualist(testerchain, coordinator_agent, cohort, alice, bob):
|
|||
# prepare message and conditions
|
||||
plaintext = PLAINTEXT.encode()
|
||||
|
||||
# create Enrico
|
||||
enrico = Enrico(encrypting_key=encrypting_key, signer=signer)
|
||||
|
||||
# encrypt
|
||||
# print(f'encrypting for DKG with key {bytes(encrypting_key.to_bytes()).hex()}')
|
||||
enrico = Enrico(encrypting_key=encrypting_key)
|
||||
print(f"encrypting for DKG with key {bytes(encrypting_key).hex()}")
|
||||
threshold_message_kit = enrico.encrypt_for_dkg(
|
||||
plaintext=plaintext, conditions=CONDITIONS
|
||||
)
|
||||
|
||||
return threshold_message_kit
|
||||
|
||||
def test_unauthorized_decrypt(threshold_message_kit):
|
||||
"""Attempts to decrypt a message before Enrico is authorized to use the ritual"""
|
||||
print("======== DKG DECRYPTION UNAUTHORIZED ENCRYPTION ========")
|
||||
# ritual_id, ciphertext, conditions are obtained from the side channel
|
||||
bob.start_learning_loop(now=True)
|
||||
with pytest.raises(Ursula.NotEnoughUrsulas):
|
||||
_ = bob.threshold_decrypt(
|
||||
threshold_message_kit=threshold_message_kit,
|
||||
peering_timeout=0,
|
||||
)
|
||||
|
||||
return threshold_message_kit
|
||||
|
||||
def test_decrypt(threshold_message_kit):
|
||||
"""Decrypts a message and checks that it matches the original plaintext"""
|
||||
# authorize Enrico to encrypt for ritual
|
||||
tx = global_allow_list.functions.authorize(
|
||||
RITUAL_ID, [signer.accounts[0]]
|
||||
).transact({"from": initiator.transacting_power.account})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
print("==================== DKG DECRYPTION ====================")
|
||||
# ritual_id, ciphertext, conditions are obtained from the side channel
|
||||
bob.start_learning_loop(now=True)
|
||||
cleartext = bob.threshold_decrypt(
|
||||
ritual_id=RITUAL_ID,
|
||||
threshold_message_kit=threshold_message_kit,
|
||||
peering_timeout=0,
|
||||
)
|
||||
|
@ -154,6 +190,7 @@ def test_ursula_ritualist(testerchain, coordinator_agent, cohort, alice, bob):
|
|||
block_until_dkg_finalized,
|
||||
test_finality,
|
||||
test_encrypt,
|
||||
test_unauthorized_decrypt,
|
||||
test_decrypt,
|
||||
]
|
||||
|
||||
|
|
|
@ -167,7 +167,7 @@ def test_get_participation_state_start_ritual(cohort, get_random_checksum_addres
|
|||
start_ritual_event = agent.contract.events.StartRitual()
|
||||
|
||||
# create args data
|
||||
args_dict["initiator"] = get_random_checksum_address()
|
||||
args_dict["authority"] = get_random_checksum_address()
|
||||
args_dict["participants"] = [
|
||||
get_random_checksum_address(),
|
||||
get_random_checksum_address(),
|
||||
|
@ -359,7 +359,6 @@ def test_get_participation_state_end_ritual_participation_not_already_tracked(
|
|||
end_ritual_event = agent.contract.events.EndRitual()
|
||||
|
||||
# create args data
|
||||
args_dict["initiator"] = get_random_checksum_address()
|
||||
args_dict["successful"] = True
|
||||
|
||||
# ensure that test matches latest event information
|
||||
|
@ -492,7 +491,6 @@ def test_get_participation_state_end_ritual_participation_already_tracked(
|
|||
end_ritual_event = agent.contract.events.EndRitual()
|
||||
|
||||
# create args data
|
||||
args_dict["initiator"] = get_random_checksum_address()
|
||||
args_dict["successful"] = True
|
||||
|
||||
# ensure that test matches latest event information
|
||||
|
@ -607,7 +605,7 @@ def test_get_participation_state_purge_expired_cache_entries(
|
|||
start_ritual_event = agent.contract.events.StartRitual()
|
||||
args_dict = {
|
||||
"ritualId": ritual_id_1,
|
||||
"initiator": get_random_checksum_address(),
|
||||
"authority": get_random_checksum_address(),
|
||||
"participants": [
|
||||
get_random_checksum_address(),
|
||||
get_random_checksum_address(),
|
||||
|
|
|
@ -11,7 +11,7 @@ from web3.middleware.simulate_unmined_transaction import (
|
|||
)
|
||||
|
||||
from nucypher.blockchain.eth.actors import Operator
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.blockchain.eth.trackers.pre import WorkTracker, WorkTrackerBase
|
||||
|
@ -36,7 +36,7 @@ def test_ursula_operator_confirmation(
|
|||
test_registry,
|
||||
):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -48,8 +48,8 @@ def test_ursula_operator_confirmation(
|
|||
# make an staking_providers and some stakes
|
||||
tx = threshold_staking.functions.setRoles(staking_provider).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = threshold_staking.functions.setStakes(
|
||||
staking_provider, min_authorization, 0, 0
|
||||
tx = threshold_staking.functions.authorizationIncreased(
|
||||
staking_provider, 0, min_authorization
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
|
@ -96,7 +96,7 @@ def test_ursula_operator_confirmation_autopilot(
|
|||
test_registry,
|
||||
):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -112,8 +112,8 @@ def test_ursula_operator_confirmation_autopilot(
|
|||
# make an staking_providers and some stakes
|
||||
tx = threshold_staking.functions.setRoles(staking_provider2).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = threshold_staking.functions.setStakes(
|
||||
staking_provider2, min_authorization, 0, 0
|
||||
tx = threshold_staking.functions.authorizationIncreased(
|
||||
staking_provider2, 0, min_authorization
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
|
@ -170,7 +170,7 @@ def test_work_tracker(
|
|||
test_registry,
|
||||
):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -212,8 +212,8 @@ def test_work_tracker(
|
|||
# make an staking_providers and some stakes
|
||||
tx = threshold_staking.functions.setRoles(staking_provider3).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = threshold_staking.functions.setStakes(
|
||||
staking_provider3, min_authorization, 0, 0
|
||||
tx = threshold_staking.functions.authorizationIncreased(
|
||||
staking_provider3, 0, min_authorization
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from tests.constants import TEST_ETH_PROVIDER_URI
|
||||
|
||||
|
||||
def test_get_agent_with_different_registries(application_economics, test_registry, agency_local_registry):
|
||||
# Get agents using same registry instance
|
||||
application_agent_1 = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
application_agent_2 = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -21,7 +21,7 @@ def test_get_agent_with_different_registries(application_economics, test_registr
|
|||
|
||||
# Same content but different classes of registries
|
||||
application_agent_2 = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=agency_local_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@ from nucypher_core import SessionStaticSecret
|
|||
from nucypher.blockchain.eth.agents import (
|
||||
ContractAgency,
|
||||
CoordinatorAgent,
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
|
@ -27,8 +27,10 @@ def transcripts():
|
|||
return [os.urandom(32), os.urandom(32)]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("ursulas")
|
||||
@pytest.fixture(scope="module")
|
||||
def cohort(testerchain, staking_providers):
|
||||
# "ursulas" fixture is needed to set provider public key
|
||||
deployer, cohort_provider_1, cohort_provider_2, *everybody_else = staking_providers
|
||||
cohort_providers = [cohort_provider_1, cohort_provider_2]
|
||||
cohort_providers.sort() # providers must be sorted
|
||||
|
@ -36,10 +38,10 @@ def cohort(testerchain, staking_providers):
|
|||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def ursulas(cohort, test_registry):
|
||||
def cohort_ursulas(cohort, test_registry):
|
||||
ursulas_for_cohort = []
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -51,10 +53,10 @@ def ursulas(cohort, test_registry):
|
|||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def transacting_powers(testerchain, ursulas):
|
||||
def transacting_powers(testerchain, cohort_ursulas):
|
||||
return [
|
||||
TransactingPower(account=ursula, signer=Web3Signer(testerchain.client))
|
||||
for ursula in ursulas
|
||||
for ursula in cohort_ursulas
|
||||
]
|
||||
|
||||
|
||||
|
@ -65,12 +67,36 @@ def test_coordinator_properties(agent):
|
|||
assert not agent._proxy_name # not upgradeable
|
||||
|
||||
|
||||
def test_initiate_ritual(agent, cohort, transacting_powers):
|
||||
@pytest.mark.usefixtures("ursulas")
|
||||
def test_initiate_ritual(
|
||||
agent,
|
||||
cohort,
|
||||
get_random_checksum_address,
|
||||
global_allow_list,
|
||||
transacting_powers,
|
||||
ritual_token,
|
||||
testerchain,
|
||||
initiator,
|
||||
):
|
||||
number_of_rituals = agent.number_of_rituals()
|
||||
assert number_of_rituals == 0
|
||||
|
||||
duration = 60 * 60 * 24
|
||||
amount = agent.get_ritual_initiation_cost(cohort, duration)
|
||||
|
||||
# Approve the ritual token for the coordinator agent to spend
|
||||
tx = ritual_token.functions.approve(agent.contract_address, amount).transact(
|
||||
{"from": initiator.transacting_power.account}
|
||||
)
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
authority = get_random_checksum_address()
|
||||
receipt = agent.initiate_ritual(
|
||||
providers=cohort, transacting_power=transacting_powers[0]
|
||||
providers=cohort,
|
||||
authority=authority,
|
||||
duration=duration,
|
||||
access_controller=global_allow_list.address,
|
||||
transacting_power=initiator.transacting_power,
|
||||
)
|
||||
assert receipt['status'] == 1
|
||||
start_ritual_event = agent.contract.events.StartRitual().process_receipt(receipt)
|
||||
|
@ -81,7 +107,7 @@ def test_initiate_ritual(agent, cohort, transacting_powers):
|
|||
ritual_id = number_of_rituals - 1
|
||||
|
||||
ritual = agent.get_ritual(ritual_id)
|
||||
assert ritual.initiator == transacting_powers[0].account
|
||||
assert ritual.authority == authority
|
||||
|
||||
participants = agent.get_participants(ritual_id)
|
||||
assert [p.provider for p in participants] == cohort
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
from collections import Counter
|
||||
|
||||
import pytest
|
||||
import random
|
||||
from collections import Counter
|
||||
from itertools import permutations
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from nucypher.blockchain.eth.actors import Operator
|
||||
from nucypher.blockchain.eth.agents import WeightedSampler, ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.agents import (
|
||||
ContractAgency,
|
||||
TACoApplicationAgent,
|
||||
WeightedSampler,
|
||||
)
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
from nucypher.crypto.powers import TransactingPower, CryptoPower
|
||||
from nucypher.crypto.powers import CryptoPower, TransactingPower
|
||||
from tests.constants import TEST_ETH_PROVIDER_URI
|
||||
|
||||
|
||||
|
@ -18,7 +22,7 @@ def test_sampling_distribution(testerchain, test_registry, threshold_staking, ap
|
|||
|
||||
# setup
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -33,7 +37,9 @@ def test_sampling_distribution(testerchain, test_registry, threshold_staking, ap
|
|||
# initialize threshold stake
|
||||
tx = threshold_staking.functions.setRoles(provider_address).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = threshold_staking.functions.setStakes(provider_address, amount, 0, 0).transact()
|
||||
tx = threshold_staking.functions.authorizationIncreased(
|
||||
provider_address, 0, amount
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
power = TransactingPower(account=provider_address, signer=Web3Signer(testerchain.client))
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import os
|
||||
import pytest
|
||||
from eth_utils import to_checksum_address, is_address
|
||||
|
||||
from nucypher.blockchain.eth.agents import PREApplicationAgent, ContractAgency
|
||||
import pytest
|
||||
from eth_utils import is_address, to_checksum_address
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
|
@ -15,7 +16,7 @@ MIN_SECONDS = 1
|
|||
|
||||
def test_get_min_authorization(test_registry, application_economics):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -25,7 +26,7 @@ def test_get_min_authorization(test_registry, application_economics):
|
|||
|
||||
def test_get_min_seconds(test_registry, application_economics):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -37,12 +38,14 @@ def test_authorized_tokens(
|
|||
testerchain, application_economics, test_registry, staking_providers
|
||||
):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
provider_account = staking_providers[0]
|
||||
authorized_amount = application_agent.get_authorized_stake(staking_provider=provider_account)
|
||||
authorized_amount = application_agent.get_authorized_stake(
|
||||
staking_provider=provider_account
|
||||
)
|
||||
assert authorized_amount >= application_economics.min_authorization
|
||||
|
||||
|
||||
|
@ -50,7 +53,7 @@ def test_staking_providers_and_operators_relationships(
|
|||
testerchain, test_registry, threshold_staking, application_economics
|
||||
):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -58,41 +61,63 @@ def test_staking_providers_and_operators_relationships(
|
|||
staking_provider_account, operator_account, *other = testerchain.unassigned_accounts
|
||||
tx = threshold_staking.functions.setRoles(staking_provider_account).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = threshold_staking.functions.setStakes(staking_provider_account, application_economics.min_authorization, 0, 0).transact()
|
||||
tx = threshold_staking.functions.authorizationIncreased(
|
||||
staking_provider_account, 0, application_economics.min_authorization
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# The staking provider hasn't bond an operator yet
|
||||
assert NULL_ADDRESS == application_agent.get_operator_from_staking_provider(staking_provider=staking_provider_account)
|
||||
assert NULL_ADDRESS == application_agent.get_operator_from_staking_provider(
|
||||
staking_provider=staking_provider_account
|
||||
)
|
||||
|
||||
tpower = TransactingPower(account=staking_provider_account, signer=Web3Signer(testerchain.client))
|
||||
_txhash = application_agent.bond_operator(transacting_power=tpower,
|
||||
staking_provider=staking_provider_account,
|
||||
operator=operator_account)
|
||||
tpower = TransactingPower(
|
||||
account=staking_provider_account, signer=Web3Signer(testerchain.client)
|
||||
)
|
||||
_txhash = application_agent.bond_operator(
|
||||
transacting_power=tpower,
|
||||
staking_provider=staking_provider_account,
|
||||
operator=operator_account,
|
||||
)
|
||||
|
||||
# We can check the staker-worker relation from both sides
|
||||
assert operator_account == application_agent.get_operator_from_staking_provider(staking_provider=staking_provider_account)
|
||||
assert staking_provider_account == application_agent.get_staking_provider_from_operator(operator_address=operator_account)
|
||||
assert operator_account == application_agent.get_operator_from_staking_provider(
|
||||
staking_provider=staking_provider_account
|
||||
)
|
||||
assert (
|
||||
staking_provider_account
|
||||
== application_agent.get_staking_provider_from_operator(
|
||||
operator_address=operator_account
|
||||
)
|
||||
)
|
||||
|
||||
# No staker-worker relationship
|
||||
random_address = to_checksum_address(os.urandom(20))
|
||||
assert NULL_ADDRESS == application_agent.get_operator_from_staking_provider(staking_provider=random_address)
|
||||
assert NULL_ADDRESS == application_agent.get_staking_provider_from_operator(operator_address=random_address)
|
||||
assert NULL_ADDRESS == application_agent.get_operator_from_staking_provider(
|
||||
staking_provider=random_address
|
||||
)
|
||||
assert NULL_ADDRESS == application_agent.get_staking_provider_from_operator(
|
||||
operator_address=random_address
|
||||
)
|
||||
|
||||
|
||||
def test_get_staker_population(staking_providers, test_registry):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
||||
# Apart from all the providers in the fixture, we also added a new provider above
|
||||
assert application_agent.get_staking_providers_population() == len(staking_providers) + 1
|
||||
assert (
|
||||
application_agent.get_staking_providers_population()
|
||||
== len(staking_providers) + 1
|
||||
)
|
||||
|
||||
|
||||
def test_get_swarm(staking_providers, test_registry):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -110,22 +135,26 @@ def test_get_swarm(staking_providers, test_registry):
|
|||
@pytest.mark.usefixtures("staking_providers")
|
||||
def test_sample_staking_providers(test_registry):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
||||
providers_population = application_agent.get_staking_providers_population()
|
||||
|
||||
with pytest.raises(PREApplicationAgent.NotEnoughStakingProviders):
|
||||
application_agent.get_staking_provider_reservoir().draw(providers_population + 1) # One more than we have deployed
|
||||
with pytest.raises(TACoApplicationAgent.NotEnoughStakingProviders):
|
||||
application_agent.get_staking_provider_reservoir().draw(
|
||||
providers_population + 1
|
||||
) # One more than we have deployed
|
||||
|
||||
providers = application_agent.get_staking_provider_reservoir().draw(3)
|
||||
assert len(providers) == 3 # Three...
|
||||
assert len(providers) == 3 # Three...
|
||||
assert len(set(providers)) == 3 # ...unique addresses
|
||||
|
||||
# Same but with pagination
|
||||
providers = application_agent.get_staking_provider_reservoir(pagination_size=1).draw(3)
|
||||
providers = application_agent.get_staking_provider_reservoir(
|
||||
pagination_size=1
|
||||
).draw(3)
|
||||
assert len(providers) == 3
|
||||
assert len(set(providers)) == 3
|
||||
light = application_agent.blockchain.is_light
|
||||
|
@ -138,12 +167,14 @@ def test_sample_staking_providers(test_registry):
|
|||
|
||||
def test_get_staking_provider_info(testerchain, test_registry):
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
staking_provider_account, operator_account, *other = testerchain.unassigned_accounts
|
||||
info: StakingProviderInfo = application_agent.get_staking_provider_info(staking_provider=staking_provider_account)
|
||||
info: StakingProviderInfo = application_agent.get_staking_provider_info(
|
||||
staking_provider=staking_provider_account
|
||||
)
|
||||
assert info.operator_start_timestamp > 0
|
||||
assert info.operator == operator_account
|
||||
assert not info.operator_confirmed
|
|
@ -5,8 +5,8 @@ plugins:
|
|||
|
||||
dependencies:
|
||||
- name: nucypher-contracts
|
||||
github: nucypher/nucypher-contracts
|
||||
ref: main
|
||||
github: derekpierre/nucypher-contracts
|
||||
ref: alpha-tweaks
|
||||
- name: openzeppelin
|
||||
github: OpenZeppelin/openzeppelin-contracts
|
||||
version: 4.8.1
|
||||
|
@ -20,19 +20,39 @@ solidity:
|
|||
deployments:
|
||||
ethereum:
|
||||
local:
|
||||
- contract_type: RitualToken
|
||||
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # deployer account at index 0
|
||||
total_supply: 1000000000000000000000000000
|
||||
- contract_type: TToken
|
||||
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # deployer account at index 0
|
||||
total_supply: 1000000000000000000000000000
|
||||
- contract_type: NuCypherToken
|
||||
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # test account at index 0
|
||||
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # deployer account at index 0
|
||||
nu_token_supply: 1_000_000_000
|
||||
- contract_type: SimplePREApplication
|
||||
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # test account at index 0
|
||||
threshold_staking: '::ThresholdStakingForPREApplicationMock.address::'
|
||||
- contract_type: StakeInfo
|
||||
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # deployer account at index 0
|
||||
updaters:
|
||||
- '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # deployer account at index 0
|
||||
- contract_type: TACoApplication
|
||||
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # deployer account at index 0
|
||||
t_token: <TToken.address>
|
||||
threshold_staking: <ThresholdStakingForTACoApplicationMock.address>
|
||||
pre_min_authorization: 40000000000000000000000
|
||||
pre_min_operator_seconds: 86400 # one day in seconds
|
||||
reward_duration: 604800
|
||||
deauthorization_duration: 5184000
|
||||
- contract_type: Coordinator
|
||||
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # test account at index 0
|
||||
app: '::SimplePREApplication.address::'
|
||||
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # deployer account at index 0
|
||||
stake_info: <StakeInfo.address>
|
||||
ritual_timeout: 3600
|
||||
max_dkg_size: 8
|
||||
admin: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # deployer account at index 0
|
||||
currency: <RitualToken.address>
|
||||
fee: 1
|
||||
- contract_type: GlobalAllowList
|
||||
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # deployer account at index 0
|
||||
coordinator: <Coordinator.address>
|
||||
admin: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # deployer account at index 0
|
||||
|
||||
test:
|
||||
mnemonic: test test test test test test test test test test test junk
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
dependencies:
|
||||
- name: nucypher-contracts
|
||||
github: nucypher/nucypher-contracts
|
||||
ref: main
|
||||
contracts_folder: src
|
|
@ -4,7 +4,7 @@ from nucypher_core import MetadataResponse, MetadataResponsePayload
|
|||
from twisted.logger import LogLevel, globalLogPublisher
|
||||
|
||||
from nucypher.acumen.perception import FleetSensor
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
|
@ -91,7 +91,7 @@ def test_invalid_operators_tolerance(
|
|||
# Setup
|
||||
#
|
||||
application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
@ -112,8 +112,8 @@ def test_invalid_operators_tolerance(
|
|||
min_authorization = application_economics.min_authorization
|
||||
tx = threshold_staking.functions.setRoles(_staking_provider).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = threshold_staking.functions.setStakes(
|
||||
_staking_provider, min_authorization, 0, 0
|
||||
tx = threshold_staking.functions.authorizationIncreased(
|
||||
_staking_provider, 0, min_authorization
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import datetime
|
|||
import maya
|
||||
import pytest
|
||||
from eth_account._utils.signing import to_standard_signature_bytes
|
||||
from web3 import Web3
|
||||
|
||||
from nucypher.characters.lawful import Enrico, Ursula
|
||||
from nucypher.characters.unlawful import Vladimir
|
||||
|
@ -12,6 +13,18 @@ from tests.constants import MOCK_ETH_PROVIDER_URI, TEST_ETH_PROVIDER_URI
|
|||
from tests.utils.middleware import NodeIsDownMiddleware
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vladimir_needs_eth(testerchain):
|
||||
# transfer some ETH to Vladimir who needs it to set provider public key
|
||||
tx = {
|
||||
"to": Vladimir.fraud_address,
|
||||
"from": testerchain.etherbase_account,
|
||||
"value": Web3.to_wei("1", "ether"),
|
||||
}
|
||||
txhash = testerchain.w3.eth.send_transaction(tx)
|
||||
_ = testerchain.wait_for_receipt(txhash)
|
||||
|
||||
|
||||
def test_stakers_bond_to_ursulas(ursulas, test_registry, staking_providers):
|
||||
assert len(ursulas) == len(staking_providers)
|
||||
for ursula in ursulas:
|
||||
|
@ -48,7 +61,10 @@ def remote_vladimir(**kwds):
|
|||
return remote_vladimir
|
||||
|
||||
|
||||
def test_vladimir_cannot_verify_interface_with_ursulas_signing_key(ursulas, test_registry_source_manager):
|
||||
@pytest.mark.usefixtures("vladimir_needs_eth")
|
||||
def test_vladimir_cannot_verify_interface_with_ursulas_signing_key(
|
||||
testerchain, ursulas, test_registry_source_manager
|
||||
):
|
||||
his_target = list(ursulas)[4]
|
||||
|
||||
# Vladimir has his own ether address; he hopes to publish it along with Ursula's details
|
||||
|
@ -73,6 +89,7 @@ def test_vladimir_cannot_verify_interface_with_ursulas_signing_key(ursulas, test
|
|||
vladimir.validate_metadata()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vladimir_needs_eth")
|
||||
def test_vladimir_uses_his_own_signing_key(alice, ursulas, test_registry):
|
||||
"""
|
||||
Similar to the attack above, but this time Vladimir makes his own interface signature
|
||||
|
@ -108,6 +125,7 @@ def test_vladimir_uses_his_own_signing_key(alice, ursulas, test_registry):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vladimir_needs_eth")
|
||||
def test_vladimir_invalidity_without_stake(testerchain, ursulas, alice):
|
||||
his_target = list(ursulas)[4]
|
||||
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
import pytest
|
||||
from eth_account._utils.legacy_transactions import Transaction
|
||||
from eth_utils import to_checksum_address
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.characters.lawful import Character
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
from tests.constants import TEST_ETH_PROVIDER_URI
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.crypto.utils import verify_eip_191
|
||||
from tests.conftest import LOCK_FUNCTION
|
||||
from tests.constants import INSECURE_DEVELOPMENT_PASSWORD, TEST_ETH_PROVIDER_URI
|
||||
|
||||
TransactingPower.lock_account = LOCK_FUNCTION
|
||||
|
||||
|
||||
def test_character_transacting_power_signing(testerchain, test_registry):
|
||||
|
@ -49,19 +60,6 @@ def test_character_transacting_power_signing(testerchain, test_registry):
|
|||
restored_dict = restored_transaction.as_dict()
|
||||
assert to_checksum_address(restored_dict['to']) == transaction_dict['to']
|
||||
|
||||
import pytest
|
||||
from eth_account._utils.legacy_transactions import Transaction
|
||||
from eth_utils import to_checksum_address
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.crypto.utils import verify_eip_191
|
||||
from tests.conftest import LOCK_FUNCTION
|
||||
from tests.constants import INSECURE_DEVELOPMENT_PASSWORD
|
||||
|
||||
TransactingPower.lock_account = LOCK_FUNCTION
|
||||
|
||||
|
||||
def test_transacting_power_sign_message(testerchain):
|
||||
|
||||
|
@ -123,7 +121,7 @@ def test_transacting_power_sign_transaction(testerchain):
|
|||
|
||||
def test_transacting_power_sign_agent_transaction(testerchain, test_registry):
|
||||
agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from eth_account import Account
|
||||
from web3 import Web3
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from nucypher.blockchain.eth.signers import KeystoreSigner
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.cli.main import nucypher_cli
|
||||
|
@ -59,7 +59,7 @@ def mock_funded_account_password_keystore(
|
|||
tx = threshold_staking.functions.setRoles(provider_address).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = threshold_staking.functions.setStakes(
|
||||
provider_address, application_economics.min_authorization, 0, 0
|
||||
provider_address, 0, application_economics.min_authorization
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
|
@ -68,12 +68,12 @@ def mock_funded_account_password_keystore(
|
|||
)
|
||||
provider_power.unlock(password=INSECURE_DEVELOPMENT_PASSWORD)
|
||||
|
||||
pre_application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
taco_application_agent = ContractAgency.get_agent(
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
pre_application_agent.bond_operator(
|
||||
taco_application_agent.bond_operator(
|
||||
staking_provider=provider_address,
|
||||
operator=account.address,
|
||||
transacting_power=provider_power,
|
||||
|
|
|
@ -13,8 +13,7 @@ from nucypher.policy.conditions.lingo import (
|
|||
OrCompoundCondition,
|
||||
ReturnValueTest,
|
||||
)
|
||||
from tests.acceptance.constants import APE_TEST_CHAIN_ID
|
||||
from tests.constants import TEST_ETH_PROVIDER_URI
|
||||
from tests.constants import APE_TEST_CHAIN_ID, TEST_ETH_PROVIDER_URI
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
@ -67,11 +66,11 @@ def erc20_evm_condition_balanceof(testerchain, test_registry):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def erc721_contract(accounts, project, test_registry):
|
||||
def erc721_contract(accounts, project):
|
||||
account = accounts[0]
|
||||
|
||||
# deploy contract
|
||||
deployed_contract = account.deploy(project.ConditionNFT)
|
||||
deployed_contract = project.ConditionNFT.deploy(sender=account)
|
||||
|
||||
# mint nft with token id = 1
|
||||
deployed_contract.mint(account.address, 1, sender=account)
|
||||
|
|
|
@ -25,8 +25,8 @@ from nucypher.policy.conditions.exceptions import (
|
|||
RPCExecutionFailed,
|
||||
)
|
||||
from nucypher.policy.conditions.lingo import ConditionLingo, ReturnValueTest
|
||||
from tests.acceptance.constants import APE_TEST_CHAIN_ID
|
||||
from tests.constants import (
|
||||
APE_TEST_CHAIN_ID,
|
||||
TEST_ETH_PROVIDER_URI,
|
||||
TEST_POLYGON_PROVIDER_URI,
|
||||
)
|
||||
|
|
|
@ -5,7 +5,11 @@ import pytest
|
|||
from web3 import Web3
|
||||
|
||||
from nucypher.blockchain.eth.actors import Operator, Ritualist
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.agents import (
|
||||
ContractAgency,
|
||||
CoordinatorAgent,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.networks import NetworksInventory
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
|
@ -17,12 +21,14 @@ from nucypher.policy.conditions.lingo import ConditionLingo, ReturnValueTest
|
|||
from nucypher.policy.conditions.time import TimeCondition
|
||||
from nucypher.policy.payment import SubscriptionManagerPayment
|
||||
from nucypher.utilities.logging import Logger
|
||||
from tests.acceptance.constants import APE_TEST_CHAIN_ID
|
||||
from tests.constants import (
|
||||
APE_TEST_CHAIN_ID,
|
||||
BONUS_TOKENS_FOR_TESTS,
|
||||
GLOBAL_ALLOW_LIST,
|
||||
INSECURE_DEVELOPMENT_PASSWORD,
|
||||
MIN_STAKE_FOR_TESTS,
|
||||
MOCK_STAKING_CONTRACT_NAME,
|
||||
RITUAL_TOKEN,
|
||||
STAKE_INFO,
|
||||
TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
from tests.utils.ape import (
|
||||
|
@ -65,6 +71,11 @@ def mock_multichain_configuration(module_mocker, testerchain):
|
|||
|
||||
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def test_contracts(project):
|
||||
return project.contracts
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def nucypher_contracts(project):
|
||||
nucypher_contracts_dependency_api = project.dependencies["nucypher-contracts"]
|
||||
# simply use first entry - could be from github ('main') or local ('local')
|
||||
|
@ -74,13 +85,32 @@ def nucypher_contracts(project):
|
|||
|
||||
|
||||
@pytest.fixture(scope='module', autouse=True)
|
||||
def deploy_contracts(nucypher_contracts, accounts):
|
||||
def deploy_contracts(nucypher_contracts, test_contracts, accounts):
|
||||
deployments = ape_deploy_contracts(
|
||||
nucypher_contracts=nucypher_contracts, accounts=accounts
|
||||
nucypher_contracts=nucypher_contracts,
|
||||
test_contracts=test_contracts,
|
||||
accounts=accounts,
|
||||
)
|
||||
return deployments
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def deployer_account(accounts):
|
||||
return accounts[0]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def initiator(testerchain, alice, ritual_token, deployer_account):
|
||||
"""Returns the Initiator, funded with RitualToken"""
|
||||
# transfer ritual token to alice (initiator)
|
||||
tx = ritual_token.functions.transfer(
|
||||
alice.transacting_power.account,
|
||||
Web3.to_wei(1, "ether"),
|
||||
).transact({"from": deployer_account.address})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
return alice
|
||||
|
||||
|
||||
@pytest.fixture(scope='module', autouse=True)
|
||||
def test_registry(nucypher_contracts, deploy_contracts):
|
||||
registry = registry_from_ape_deployments(nucypher_contracts, deployments=deploy_contracts)
|
||||
|
@ -97,23 +127,68 @@ def testerchain(project, test_registry) -> TesterBlockchain:
|
|||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def threshold_staking(testerchain, test_registry):
|
||||
result = test_registry.search(contract_name=MOCK_STAKING_CONTRACT_NAME)[0]
|
||||
threshold_staking = testerchain.w3.eth.contract(
|
||||
address=result[2],
|
||||
abi=result[3]
|
||||
)
|
||||
return threshold_staking
|
||||
def stake_info(testerchain, test_registry):
|
||||
result = test_registry.search(contract_name=STAKE_INFO)[0]
|
||||
_stake_info = testerchain.w3.eth.contract(address=result[2], abi=result[3])
|
||||
return _stake_info
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def staking_providers(testerchain, test_registry, threshold_staking):
|
||||
pre_application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent,
|
||||
def ritual_token(testerchain, test_registry):
|
||||
result = test_registry.search(contract_name=RITUAL_TOKEN)[0]
|
||||
_ritual_token = testerchain.w3.eth.contract(address=result[2], abi=result[3])
|
||||
return _ritual_token
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def threshold_staking(testerchain, test_registry):
|
||||
result = test_registry.search(contract_name=MOCK_STAKING_CONTRACT_NAME)[0]
|
||||
_threshold_staking = testerchain.w3.eth.contract(address=result[2], abi=result[3])
|
||||
|
||||
# TODO: Relocate this to pre application setup
|
||||
taco_application_agent = ContractAgency.get_agent(
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
blockchain = pre_application_agent.blockchain
|
||||
|
||||
tx = _threshold_staking.functions.setApplication(
|
||||
taco_application_agent.contract_address
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
return _threshold_staking
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def coordinator_agent(testerchain, test_registry):
|
||||
"""Creates a coordinator agent"""
|
||||
coordinator = ContractAgency.get_agent(
|
||||
CoordinatorAgent, registry=test_registry, provider_uri=TEST_ETH_PROVIDER_URI
|
||||
)
|
||||
tx = coordinator.contract.functions.makeInitiationPublic().transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
return coordinator
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def global_allow_list(testerchain, test_registry):
|
||||
result = test_registry.search(contract_name=GLOBAL_ALLOW_LIST)[0]
|
||||
_global_allow_list = testerchain.w3.eth.contract(address=result[2], abi=result[3])
|
||||
return _global_allow_list
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def staking_providers(testerchain, test_registry, threshold_staking, stake_info):
|
||||
taco_application_agent = ContractAgency.get_agent(
|
||||
TACoApplicationAgent,
|
||||
registry=test_registry,
|
||||
provider_uri=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
blockchain = taco_application_agent.blockchain
|
||||
minimum_stake = (
|
||||
taco_application_agent.contract.functions.minimumAuthorization().call()
|
||||
)
|
||||
|
||||
staking_providers = list()
|
||||
for provider_address, operator_address in zip(blockchain.stake_providers_accounts, blockchain.ursulas_accounts):
|
||||
|
@ -121,22 +196,28 @@ def staking_providers(testerchain, test_registry, threshold_staking):
|
|||
provider_power.unlock(password=INSECURE_DEVELOPMENT_PASSWORD)
|
||||
|
||||
# for a random amount
|
||||
amount = MIN_STAKE_FOR_TESTS + random.randrange(BONUS_TOKENS_FOR_TESTS)
|
||||
amount = minimum_stake + random.randrange(BONUS_TOKENS_FOR_TESTS)
|
||||
|
||||
# initialize threshold stake
|
||||
# initialize threshold stake via threshold staking (permission-less mock)
|
||||
tx = threshold_staking.functions.setRoles(provider_address).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = threshold_staking.functions.setStakes(provider_address, amount, 0, 0).transact()
|
||||
|
||||
# TODO: extract this to a fixture
|
||||
tx = threshold_staking.functions.authorizationIncreased(
|
||||
provider_address, 0, amount
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# We assume that the staking provider knows in advance the account of her operator
|
||||
pre_application_agent.bond_operator(staking_provider=provider_address,
|
||||
operator=operator_address,
|
||||
transacting_power=provider_power)
|
||||
taco_application_agent.bond_operator(
|
||||
staking_provider=provider_address,
|
||||
operator=operator_address,
|
||||
transacting_power=provider_power,
|
||||
)
|
||||
|
||||
operator_power = TransactingPower(
|
||||
account=operator_address, signer=Web3Signer(testerchain.client)
|
||||
)
|
||||
|
||||
operator = Operator(
|
||||
is_me=True,
|
||||
operator_address=operator_address,
|
||||
|
@ -154,6 +235,20 @@ def staking_providers(testerchain, test_registry, threshold_staking):
|
|||
)
|
||||
operator.confirm_address() # assume we always need a "pre-confirmed" operator for now.
|
||||
|
||||
# TODO clean this up, perhaps with a fixture
|
||||
# update StakeInfo
|
||||
tx = stake_info.functions.updateOperator(
|
||||
provider_address,
|
||||
operator_address,
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
tx = stake_info.functions.updateAmount(
|
||||
provider_address,
|
||||
amount,
|
||||
).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# track
|
||||
staking_providers.append(provider_address)
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
from ape.utils import DEFAULT_TEST_CHAIN_ID
|
||||
|
||||
APE_TEST_CHAIN_ID = DEFAULT_TEST_CHAIN_ID # APE uses this chain id
|
|
@ -0,0 +1,14 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
/**
|
||||
* @notice Contract for testing the application contract
|
||||
*/
|
||||
contract RitualToken is ERC20("RitualToken", "RT") {
|
||||
constructor(uint256 _totalSupplyOfTokens) {
|
||||
_mint(msg.sender, _totalSupplyOfTokens);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ from datetime import datetime
|
|||
from pathlib import Path
|
||||
from random import SystemRandom
|
||||
|
||||
from ape.utils import DEFAULT_TEST_CHAIN_ID
|
||||
from web3 import Web3
|
||||
|
||||
from nucypher.blockchain.eth.token import NU
|
||||
|
@ -17,7 +18,19 @@ from nucypher.config.constants import (
|
|||
#
|
||||
|
||||
|
||||
MOCK_STAKING_CONTRACT_NAME = 'ThresholdStakingForPREApplicationMock'
|
||||
MOCK_STAKING_CONTRACT_NAME = "ThresholdStakingForTACoApplicationMock"
|
||||
RITUAL_TOKEN = "RitualToken"
|
||||
T_TOKEN = "TToken"
|
||||
STAKE_INFO = "StakeInfo"
|
||||
CONDITION_NFT = "ConditionNFT"
|
||||
GLOBAL_ALLOW_LIST = "GlobalAllowList"
|
||||
|
||||
|
||||
#
|
||||
# Ape
|
||||
#
|
||||
|
||||
APE_TEST_CHAIN_ID = DEFAULT_TEST_CHAIN_ID # ape uses this chain id
|
||||
|
||||
#
|
||||
# Ursula
|
||||
|
@ -62,6 +75,8 @@ DEVELOPMENT_ETH_AIRDROP_AMOUNT = int(Web3().to_wei(100, 'ether'))
|
|||
|
||||
NUMBER_OF_ALLOCATIONS_IN_TESTS = 50 # TODO: Move to constants
|
||||
|
||||
TESTERCHAIN_CHAIN_ID = 131277322940537
|
||||
|
||||
|
||||
#
|
||||
# Insecure Secrets
|
||||
|
@ -136,7 +151,6 @@ CLI_TEST_ENV = {NUCYPHER_ENVVAR_KEYSTORE_PASSWORD: INSECURE_DEVELOPMENT_PASSWORD
|
|||
|
||||
CLI_ENV = {NUCYPHER_ENVVAR_KEYSTORE_PASSWORD: INSECURE_DEVELOPMENT_PASSWORD,
|
||||
NUCYPHER_ENVVAR_OPERATOR_ETH_PASSWORD: INSECURE_DEVELOPMENT_PASSWORD}
|
||||
TESTERCHAIN_CHAIN_ID = 131277322940537
|
||||
|
||||
|
||||
#
|
||||
|
|
|
@ -19,6 +19,7 @@ from web3 import Web3
|
|||
|
||||
import tests
|
||||
from nucypher.blockchain.economics import Economics
|
||||
from nucypher.blockchain.eth.actors import Operator
|
||||
from nucypher.blockchain.eth.clients import EthereumClient
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.registry import LocalContractRegistry
|
||||
|
@ -341,6 +342,11 @@ def light_ursula(temp_dir_path, test_registry_source_manager, random_account, mo
|
|||
payment_method = SubscriptionManagerPayment(
|
||||
eth_provider=MOCK_ETH_PROVIDER_URI, network=TEMPORARY_DOMAIN
|
||||
)
|
||||
|
||||
mocker.patch.object(
|
||||
Operator, "get_staking_provider_address", return_value=random_account.address
|
||||
)
|
||||
|
||||
ursula = Ursula(
|
||||
rest_host=LOOPBACK_ADDRESS,
|
||||
rest_port=select_test_port(),
|
||||
|
|
|
@ -5,13 +5,13 @@ from unittest.mock import PropertyMock, patch
|
|||
import pytest
|
||||
import pytest_twisted
|
||||
from eth_typing import ChecksumAddress
|
||||
from nucypher_core.ferveo import FerveoVariant
|
||||
from twisted.internet.threads import deferToThread
|
||||
from web3.datastructures import AttributeDict
|
||||
from nucypher_core.ferveo import FerveoVariant
|
||||
|
||||
from nucypher.blockchain.eth.agents import CoordinatorAgent
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.characters.lawful import Enrico, Ursula
|
||||
from nucypher.crypto.ferveo.dkg import FerveoVariant
|
||||
from nucypher.policy.conditions.lingo import ConditionLingo
|
||||
from tests.constants import TESTERCHAIN_CHAIN_ID
|
||||
from tests.mock.coordinator import MockCoordinatorAgent
|
||||
|
@ -74,7 +74,7 @@ def cohort(ursulas, mock_coordinator_agent):
|
|||
return ursulas
|
||||
|
||||
|
||||
def execute_round_1(ritual_id: int, initiator: ChecksumAddress, cohort: List[Ursula]):
|
||||
def execute_round_1(ritual_id: int, authority: ChecksumAddress, cohort: List[Ursula]):
|
||||
|
||||
# check that the ritual is being tracked locally upon initialization for each node
|
||||
for ursula in cohort:
|
||||
|
@ -88,7 +88,7 @@ def execute_round_1(ritual_id: int, initiator: ChecksumAddress, cohort: List[Urs
|
|||
args=AttributeDict(
|
||||
{
|
||||
"ritualId": ritual_id,
|
||||
"initiator": initiator,
|
||||
"authority": authority,
|
||||
"participants": [u.checksum_address for u in cohort],
|
||||
}
|
||||
),
|
||||
|
@ -117,6 +117,7 @@ def execute_round_2(ritual_id: int, cohort: List[Ursula]):
|
|||
ursula.ritual_tracker._handle_ritual_event(event, get_block_when=lambda x: event)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_sign_message")
|
||||
@pytest.mark.parametrize('dkg_size, ritual_id, variant', PARAMS)
|
||||
@pytest_twisted.inlineCallbacks()
|
||||
def test_ursula_ritualist(
|
||||
|
@ -129,96 +130,119 @@ def test_ursula_ritualist(
|
|||
ritual_id,
|
||||
variant,
|
||||
test_registry_source_manager,
|
||||
get_random_checksum_address,
|
||||
):
|
||||
"""Tests the DKG and the encryption/decryption of a message"""
|
||||
cohort = cohort[:dkg_size]
|
||||
|
||||
def initialize():
|
||||
"""Initiates the ritual"""
|
||||
print("==================== INITIALIZING ====================")
|
||||
cohort_staking_provider_addresses = list(u.checksum_address for u in cohort)
|
||||
mock_coordinator_agent.initiate_ritual(
|
||||
providers=cohort_staking_provider_addresses,
|
||||
transacting_power=alice.transacting_power
|
||||
)
|
||||
assert mock_coordinator_agent.number_of_rituals() == ritual_id + 1
|
||||
# adjust threshold since we are testing with pre-computed (simple is the default)
|
||||
threshold = mock_coordinator_agent.get_threshold_for_ritual_size(
|
||||
dkg_size
|
||||
) # default is simple
|
||||
if variant == FerveoVariant.Precomputed:
|
||||
threshold = dkg_size
|
||||
|
||||
def round_1(_):
|
||||
"""Checks the initialization of the ritual"""
|
||||
print("==================== CHECKING INITIALIZATION ====================")
|
||||
# verify that the ritual is in the correct state
|
||||
assert mock_coordinator_agent.get_ritual_status(ritual_id=ritual_id) == \
|
||||
mock_coordinator_agent.Ritual.Status.AWAITING_TRANSCRIPTS
|
||||
with patch.object(
|
||||
mock_coordinator_agent, "get_threshold_for_ritual_size", return_value=threshold
|
||||
):
|
||||
|
||||
ritual = mock_coordinator_agent.get_ritual(ritual_id)
|
||||
execute_round_1(ritual_id, ritual.initiator, cohort)
|
||||
|
||||
def round_2(_):
|
||||
"""simulates the passage of time and the execution of the event scanner"""
|
||||
print("==================== BLOCKING UNTIL DKG FINALIZED ====================")
|
||||
execute_round_2(ritual_id, cohort)
|
||||
|
||||
def finality(_):
|
||||
"""Checks the finality of the DKG"""
|
||||
print("==================== CHECKING DKG FINALITY ====================")
|
||||
|
||||
status = mock_coordinator_agent.get_ritual_status(ritual_id)
|
||||
assert status == mock_coordinator_agent.Ritual.Status.FINALIZED
|
||||
for ursula in cohort:
|
||||
assert ursula.dkg_storage.get_transcript(ritual_id) is not None
|
||||
|
||||
def encrypt(_):
|
||||
"""Encrypts a message and returns the ciphertext and conditions"""
|
||||
print("==================== DKG ENCRYPTION ====================")
|
||||
encrypting_key = mock_coordinator_agent.get_ritual_public_key(
|
||||
ritual_id=ritual_id
|
||||
)
|
||||
|
||||
# prepare message and conditions
|
||||
plaintext = PLAINTEXT.encode()
|
||||
|
||||
# encrypt
|
||||
# print(f'encrypting for DKG with key {bytes(encrypting_key.to_bytes()).hex()}')
|
||||
enrico = Enrico(encrypting_key=encrypting_key)
|
||||
threshold_message_kit = enrico.encrypt_for_dkg(
|
||||
plaintext=plaintext, conditions=CONDITIONS
|
||||
)
|
||||
return threshold_message_kit
|
||||
|
||||
def decrypt(threshold_message_kit):
|
||||
"""Decrypts a message and checks that it matches the original plaintext"""
|
||||
print("==================== DKG DECRYPTION ====================")
|
||||
bob.start_learning_loop(now=True)
|
||||
|
||||
# mock the use of non-default variants since it can no longer be specified
|
||||
with patch.object(
|
||||
bob, "_default_dkg_variant", new_callable=PropertyMock(return_value=variant)
|
||||
):
|
||||
cleartext = bob.threshold_decrypt(
|
||||
ritual_id=ritual_id,
|
||||
threshold_message_kit=threshold_message_kit,
|
||||
peering_timeout=0,
|
||||
def initialize():
|
||||
"""Initiates the ritual"""
|
||||
print("==================== INITIALIZING ====================")
|
||||
cohort_staking_provider_addresses = list(u.checksum_address for u in cohort)
|
||||
mock_coordinator_agent.initiate_ritual(
|
||||
providers=cohort_staking_provider_addresses,
|
||||
authority=alice.transacting_power.account,
|
||||
duration=1,
|
||||
access_controller=get_random_checksum_address(),
|
||||
transacting_power=alice.transacting_power,
|
||||
)
|
||||
assert bytes(cleartext) == PLAINTEXT.encode()
|
||||
assert mock_coordinator_agent.number_of_rituals() == ritual_id + 1
|
||||
|
||||
print("==================== DECRYPTION SUCCESSFUL ====================")
|
||||
def round_1(_):
|
||||
"""Checks the initialization of the ritual"""
|
||||
print("==================== CHECKING INITIALIZATION ====================")
|
||||
# verify that the ritual is in the correct state
|
||||
assert (
|
||||
mock_coordinator_agent.get_ritual_status(ritual_id=ritual_id)
|
||||
== mock_coordinator_agent.Ritual.Status.AWAITING_TRANSCRIPTS
|
||||
)
|
||||
|
||||
def error_handler(e):
|
||||
"""Prints the error and raises it"""
|
||||
print("==================== ERROR ====================")
|
||||
print(e.getTraceback())
|
||||
raise e
|
||||
ritual = mock_coordinator_agent.get_ritual(ritual_id)
|
||||
execute_round_1(ritual_id, ritual.authority, cohort)
|
||||
|
||||
# order matters
|
||||
d = deferToThread(initialize)
|
||||
callbacks = [
|
||||
round_1,
|
||||
round_2,
|
||||
finality,
|
||||
encrypt,
|
||||
decrypt,
|
||||
]
|
||||
for callback in callbacks:
|
||||
d.addCallback(callback)
|
||||
d.addErrback(error_handler)
|
||||
yield d
|
||||
def round_2(_):
|
||||
"""simulates the passage of time and the execution of the event scanner"""
|
||||
print(
|
||||
"==================== BLOCKING UNTIL DKG FINALIZED ===================="
|
||||
)
|
||||
execute_round_2(ritual_id, cohort)
|
||||
|
||||
def finality(_):
|
||||
"""Checks the finality of the DKG"""
|
||||
print("==================== CHECKING DKG FINALITY ====================")
|
||||
|
||||
status = mock_coordinator_agent.get_ritual_status(ritual_id)
|
||||
assert status == mock_coordinator_agent.Ritual.Status.FINALIZED
|
||||
for ursula in cohort:
|
||||
assert ursula.dkg_storage.get_transcript(ritual_id) is not None
|
||||
|
||||
def encrypt(_):
|
||||
"""Encrypts a message and returns the ciphertext and conditions"""
|
||||
print("==================== DKG ENCRYPTION ====================")
|
||||
encrypting_key = mock_coordinator_agent.get_ritual_public_key(
|
||||
ritual_id=ritual_id
|
||||
)
|
||||
|
||||
# prepare message and conditions
|
||||
plaintext = PLAINTEXT.encode()
|
||||
|
||||
# create Enrico
|
||||
signer = Web3Signer(client=testerchain.client)
|
||||
enrico = Enrico(encrypting_key=encrypting_key, signer=signer)
|
||||
|
||||
# encrypt
|
||||
print(f"encrypting for DKG with key {bytes(encrypting_key).hex()}")
|
||||
threshold_message_kit = enrico.encrypt_for_dkg(
|
||||
plaintext=plaintext, conditions=CONDITIONS
|
||||
)
|
||||
return threshold_message_kit
|
||||
|
||||
def decrypt(threshold_message_kit):
|
||||
"""Decrypts a message and checks that it matches the original plaintext"""
|
||||
print("==================== DKG DECRYPTION ====================")
|
||||
bob.start_learning_loop(now=True)
|
||||
|
||||
# mock the use of non-default variants since it can no longer be specified
|
||||
with patch.object(
|
||||
bob,
|
||||
"_default_dkg_variant",
|
||||
new_callable=PropertyMock(return_value=variant),
|
||||
):
|
||||
cleartext = bob.threshold_decrypt(
|
||||
threshold_message_kit=threshold_message_kit,
|
||||
peering_timeout=0,
|
||||
)
|
||||
assert bytes(cleartext) == PLAINTEXT.encode()
|
||||
|
||||
print("==================== DECRYPTION SUCCESSFUL ====================")
|
||||
|
||||
def error_handler(e):
|
||||
"""Prints the error and raises it"""
|
||||
print("==================== ERROR ====================")
|
||||
print(e.getTraceback())
|
||||
raise e
|
||||
|
||||
# order matters
|
||||
d = deferToThread(initialize)
|
||||
callbacks = [
|
||||
round_1,
|
||||
round_2,
|
||||
finality,
|
||||
encrypt,
|
||||
decrypt,
|
||||
]
|
||||
for callback in callbacks:
|
||||
d.addCallback(callback)
|
||||
d.addErrback(error_handler)
|
||||
yield d
|
||||
|
|
|
@ -6,4 +6,7 @@ from nucypher.blockchain.economics import EconomicsFactory
|
|||
@pytest.mark.usefixtures('testerchain')
|
||||
def test_retrieving_from_blockchain(application_economics, test_registry):
|
||||
economics = EconomicsFactory.get_economics(registry=test_registry)
|
||||
assert economics.pre_application_deployment_parameters == application_economics.pre_application_deployment_parameters
|
||||
assert (
|
||||
economics.taco_application_deployment_parameters
|
||||
== application_economics.taco_application_deployment_parameters
|
||||
)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import pytest
|
||||
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.characters.chaotic import (
|
||||
NiceGuyEddie,
|
||||
ThisBobAlwaysDecrypts,
|
||||
|
@ -14,18 +15,17 @@ from tests.constants import (
|
|||
)
|
||||
|
||||
|
||||
def _attempt_decryption(BobClass, plaintext):
|
||||
def _attempt_decryption(BobClass, plaintext, testerchain):
|
||||
trinket = 80 # Doens't matter.
|
||||
|
||||
enrico = NiceGuyEddie(encrypting_key=trinket)
|
||||
signer = Web3Signer(client=testerchain.client)
|
||||
enrico = NiceGuyEddie(encrypting_key=trinket, signer=signer)
|
||||
bob = BobClass(
|
||||
registry=MOCK_REGISTRY_FILEPATH,
|
||||
domain="lynx",
|
||||
eth_provider_uri=MOCK_ETH_PROVIDER_URI,
|
||||
)
|
||||
|
||||
ANYTHING_CAN_BE_PASSED_AS_RITUAL_DATA = 55
|
||||
|
||||
definitely_false_condition = {
|
||||
"version": ConditionLingo.VERSION,
|
||||
"condition": {
|
||||
|
@ -42,20 +42,21 @@ def _attempt_decryption(BobClass, plaintext):
|
|||
)
|
||||
|
||||
decrypted_cleartext = bob.threshold_decrypt(
|
||||
ritual_id=ANYTHING_CAN_BE_PASSED_AS_RITUAL_DATA,
|
||||
threshold_message_kit=threshold_message_kit,
|
||||
)
|
||||
|
||||
return decrypted_cleartext
|
||||
|
||||
|
||||
def test_user_controls_success():
|
||||
@pytest.mark.usefixtures("mock_sign_message")
|
||||
def test_user_controls_success(testerchain):
|
||||
plaintext = b"ever thus to deadbeats"
|
||||
result = _attempt_decryption(ThisBobAlwaysDecrypts, plaintext)
|
||||
result = _attempt_decryption(ThisBobAlwaysDecrypts, plaintext, testerchain)
|
||||
assert bytes(result) == bytes(plaintext)
|
||||
|
||||
|
||||
def test_user_controls_failure():
|
||||
@pytest.mark.usefixtures("mock_sign_message")
|
||||
def test_user_controls_failure(testerchain):
|
||||
plaintext = b"ever thus to deadbeats"
|
||||
with pytest.raises(Ursula.NotEnoughUrsulas):
|
||||
_ = _attempt_decryption(ThisBobAlwaysFails, plaintext)
|
||||
_ = _attempt_decryption(ThisBobAlwaysFails, plaintext, testerchain)
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
import json
|
||||
import secrets
|
||||
from pathlib import Path
|
||||
|
@ -30,7 +28,9 @@ def mock_account_password_keystore(tmp_path_factory):
|
|||
return account, password, keystore
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("test_registry_source_manager")
|
||||
@pytest.mark.usefixtures(
|
||||
"test_registry_source_manager", "monkeypatch_get_staking_provider_from_operator"
|
||||
)
|
||||
def test_ursula_init_with_local_keystore_signer(
|
||||
click_runner, temp_dir_path, mocker, testerchain, mock_account_password_keystore
|
||||
):
|
||||
|
|
|
@ -37,6 +37,7 @@ all_configurations = tuple(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("monkeypatch_get_staking_provider_from_operator")
|
||||
@pytest.mark.parametrize("character,configuration", characters_and_configurations)
|
||||
def test_development_character_configurations(
|
||||
character, configuration, test_registry_source_manager, mocker, testerchain
|
||||
|
|
|
@ -151,6 +151,7 @@ def test_tls_hosting_certificate_remains_the_same(temp_dir_path, mocker):
|
|||
recreated_ursula.disenchant()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_sign_message")
|
||||
def test_ritualist(temp_dir_path, testerchain, dkg_public_key):
|
||||
keystore = Keystore.generate(
|
||||
password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=temp_dir_path
|
||||
|
@ -186,11 +187,15 @@ def test_ritualist(temp_dir_path, testerchain, dkg_public_key):
|
|||
},
|
||||
}
|
||||
|
||||
# create enrico
|
||||
signer = Web3Signer(client=testerchain.client)
|
||||
enrico = Enrico(encrypting_key=dkg_public_key, signer=signer)
|
||||
|
||||
# encrypt
|
||||
enrico = Enrico(encrypting_key=dkg_public_key)
|
||||
threshold_message_kit = enrico.encrypt_for_dkg(
|
||||
plaintext=plaintext, conditions=CONDITIONS
|
||||
)
|
||||
|
||||
decryption_request = ThresholdDecryptionRequest(
|
||||
ritual_id=ritual_id,
|
||||
variant=FerveoVariant.Simple,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
from typing import Iterable, Optional
|
||||
|
||||
|
@ -10,8 +11,8 @@ from nucypher.blockchain.eth.agents import (
|
|||
AdjudicatorAgent,
|
||||
ContractAgency,
|
||||
CoordinatorAgent,
|
||||
PREApplicationAgent,
|
||||
StakingProvidersReservoir,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.interfaces import (
|
||||
BlockchainInterface,
|
||||
|
@ -20,6 +21,7 @@ from nucypher.blockchain.eth.interfaces import (
|
|||
from nucypher.blockchain.eth.networks import NetworksInventory
|
||||
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
|
||||
from nucypher.blockchain.eth.signers import KeystoreSigner
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.cli.types import ChecksumAddress
|
||||
from nucypher.config.characters import UrsulaConfiguration
|
||||
|
@ -55,15 +57,23 @@ def mock_sample_reservoir(testerchain, mock_contract_agency):
|
|||
}
|
||||
return StakingProvidersReservoir(addresses)
|
||||
|
||||
mock_agent = mock_contract_agency.get_agent(PREApplicationAgent)
|
||||
mock_agent = mock_contract_agency.get_agent(TACoApplicationAgent)
|
||||
mock_agent.get_staking_provider_reservoir = mock_reservoir
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def mock_sign_message(mocker):
|
||||
mocked_sign_message = mocker.patch.object(
|
||||
Web3Signer, "sign_message", return_value=os.urandom(32)
|
||||
)
|
||||
return mocked_sign_message
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=True)
|
||||
def mock_application_agent(
|
||||
testerchain, application_economics, mock_contract_agency, mocker
|
||||
):
|
||||
mock_agent = mock_contract_agency.get_agent(PREApplicationAgent)
|
||||
mock_agent = mock_contract_agency.get_agent(TACoApplicationAgent)
|
||||
# Handle the special case of confirming operator address, which returns a txhash due to the fire_and_forget option
|
||||
mock_agent.confirm_operator_address = mocker.Mock(
|
||||
return_value=testerchain.FAKE_TX_HASH
|
||||
|
@ -236,7 +246,6 @@ def mock_transacting_power(module_mocker, monkeymodule):
|
|||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def staking_providers(testerchain, test_registry, monkeymodule):
|
||||
|
||||
def faked(self, *args, **kwargs):
|
||||
return testerchain.stake_providers_accounts[testerchain.ursulas_accounts.index(self.transacting_power.account)]
|
||||
|
||||
|
@ -244,6 +253,15 @@ def staking_providers(testerchain, test_registry, monkeymodule):
|
|||
return testerchain.stake_providers_accounts
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def monkeypatch_get_staking_provider_from_operator(monkeymodule):
|
||||
monkeymodule.setattr(
|
||||
Operator,
|
||||
"get_staking_provider_address",
|
||||
lambda self: self.transacting_power.account,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def mock_condition_blockchains(session_mocker):
|
||||
"""adds testerchain's chain ID to permitted conditional chains"""
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import contextlib
|
||||
|
||||
import pytest
|
||||
import time
|
||||
|
||||
import maya
|
||||
import pytest
|
||||
from nucypher_core.umbral import SecretKey, Signer
|
||||
|
||||
from nucypher.characters.lawful import Ursula
|
||||
|
@ -15,7 +14,7 @@ from tests.mock.performance_mocks import (
|
|||
mock_message_verification,
|
||||
mock_metadata_validation,
|
||||
mock_secret_source,
|
||||
mock_verify_node
|
||||
mock_verify_node,
|
||||
)
|
||||
from tests.utils.ursula import MOCK_KNOWN_URSULAS_CACHE
|
||||
|
||||
|
@ -38,6 +37,7 @@ performance bottlenecks.
|
|||
"""
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("monkeypatch_get_staking_provider_from_operator")
|
||||
def test_alice_can_learn_about_a_whole_bunch_of_ursulas(highperf_mocked_alice, test_registry_source_manager):
|
||||
# During the fixture execution, Alice verified one node.
|
||||
# TODO: Consider changing this - #1449
|
||||
|
|
|
@ -4,10 +4,8 @@ from twisted.logger import LogLevel, globalLogPublisher
|
|||
|
||||
from nucypher.acumen.nicknames import Nickname
|
||||
from nucypher.acumen.perception import FleetSensor
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.characters.unlawful import Vladimir
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
from nucypher.types import StakingProviderInfo
|
||||
from tests.constants import MOCK_ETH_PROVIDER_URI
|
||||
from tests.utils.middleware import MockRestMiddleware
|
||||
|
||||
|
@ -43,6 +41,7 @@ def test_alice_finds_ursula_via_rest(alice, ursulas):
|
|||
assert ursula in alice.known_nodes
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("monkeypatch_get_staking_provider_from_operator")
|
||||
def test_vladimir_illegal_interface_key_does_not_propagate(ursulas, test_registry_source_manager):
|
||||
"""
|
||||
Although Ursulas propagate each other's interface information, as demonstrated above,
|
||||
|
@ -86,7 +85,7 @@ def test_vladimir_illegal_interface_key_does_not_propagate(ursulas, test_registr
|
|||
other_ursula._current_teacher_node = vladimir_as_learned
|
||||
|
||||
globalLogPublisher.addObserver(warning_trapper)
|
||||
result = other_ursula.learn_from_teacher_node()
|
||||
other_ursula.learn_from_teacher_node()
|
||||
globalLogPublisher.removeObserver(warning_trapper)
|
||||
|
||||
# Indeed, Ursula noticed that something was up.
|
||||
|
|
|
@ -4,7 +4,7 @@ from typing import List
|
|||
|
||||
import pytest
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from nucypher.types import StakingProviderInfo
|
||||
from tests.constants import MOCK_ETH_PROVIDER_URI
|
||||
|
||||
|
@ -107,8 +107,8 @@ def test_staking_provider_metrics_collector(test_registry, staking_providers, mo
|
|||
collector.initialize(metrics_prefix=prefix, registry=collector_registry)
|
||||
collector.collect()
|
||||
|
||||
pre_application_agent = ContractAgency.get_agent(
|
||||
PREApplicationAgent, registry=test_registry
|
||||
taco_application_agent = ContractAgency.get_agent(
|
||||
TACoApplicationAgent, registry=test_registry
|
||||
)
|
||||
|
||||
active_stake = collector_registry.get_sample_value(
|
||||
|
@ -117,13 +117,13 @@ def test_staking_provider_metrics_collector(test_registry, staking_providers, mo
|
|||
# only floats can be stored
|
||||
assert active_stake == float(
|
||||
int(
|
||||
pre_application_agent.get_authorized_stake(
|
||||
taco_application_agent.get_authorized_stake(
|
||||
staking_provider=staking_provider_address
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
staking_provider_info = pre_application_agent.get_staking_provider_info(
|
||||
staking_provider_info = taco_application_agent.get_staking_provider_info(
|
||||
staking_provider=staking_provider_address
|
||||
)
|
||||
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import time
|
||||
from enum import Enum
|
||||
from typing import Dict, List
|
||||
from typing import Dict, List, NamedTuple
|
||||
|
||||
from eth_typing import ChecksumAddress
|
||||
from eth_utils import keccak
|
||||
from nucypher_core import SessionStaticKey
|
||||
from nucypher_core.ferveo import AggregatedTranscript, DkgPublicKey, Transcript
|
||||
from nucypher_core.ferveo import (
|
||||
AggregatedTranscript,
|
||||
DkgPublicKey,
|
||||
FerveoPublicKey,
|
||||
Transcript,
|
||||
)
|
||||
from web3.types import TxReceipt
|
||||
|
||||
from nucypher.blockchain.eth.agents import CoordinatorAgent
|
||||
|
@ -20,6 +25,11 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
Ritual = CoordinatorAgent.Ritual
|
||||
RitualStatus = CoordinatorAgent.Ritual.Status
|
||||
G1Point = CoordinatorAgent.Ritual.G1Point
|
||||
G2Point = CoordinatorAgent.G2Point
|
||||
|
||||
class ParticipantKey(NamedTuple):
|
||||
lastRitualId: int
|
||||
publicKey: CoordinatorAgent.G2Point
|
||||
|
||||
EVENTS = {}
|
||||
rituals = []
|
||||
|
@ -35,6 +45,7 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
# Note that the call to super() is not necessary here
|
||||
|
||||
self._operator_to_staking_provider = {}
|
||||
self._participant_keys_history = {}
|
||||
|
||||
def _add_operator_to_staking_provider_mapping(
|
||||
self, mapping: Dict[ChecksumAddress, ChecksumAddress]
|
||||
|
@ -47,6 +58,11 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
except KeyError:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_threshold_for_ritual_size(cls, dkg_size: int):
|
||||
# default is simple (same as Coordinator contract)
|
||||
return dkg_size // 2 + 1
|
||||
|
||||
def emit_event(self, ritual_id: int, signal: Events, **kwargs) -> None:
|
||||
self.EVENTS[(int(time.time_ns()), ritual_id)] = (signal, {**kwargs, 'ritual_id': ritual_id})
|
||||
|
||||
|
@ -60,22 +76,33 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
#
|
||||
|
||||
def initiate_ritual(
|
||||
self, providers: List[ChecksumAddress], transacting_power: TransactingPower
|
||||
self,
|
||||
providers: List[ChecksumAddress],
|
||||
authority: ChecksumAddress,
|
||||
duration: int,
|
||||
access_controller: ChecksumAddress,
|
||||
transacting_power: TransactingPower,
|
||||
) -> TxReceipt:
|
||||
ritual_id = len(self.rituals)
|
||||
init_timestamp = int(time.time_ns())
|
||||
end_timestamp = init_timestamp + duration
|
||||
ritual = self.Ritual(
|
||||
init_timestamp=int(time.time_ns()),
|
||||
initiator=transacting_power.account,
|
||||
authority=authority,
|
||||
access_controller=access_controller,
|
||||
init_timestamp=init_timestamp,
|
||||
end_timestamp=end_timestamp,
|
||||
participants=[
|
||||
self.Participant(provider=provider) for provider in providers
|
||||
],
|
||||
dkg_size=len(providers),
|
||||
initiator=transacting_power.account,
|
||||
threshold=self.get_threshold_for_ritual_size(len(providers)),
|
||||
)
|
||||
self.rituals.append(ritual)
|
||||
self.emit_event(
|
||||
signal=self.Events.START_RITUAL,
|
||||
ritual_id=ritual_id,
|
||||
initiator=transacting_power.account,
|
||||
authority=authority,
|
||||
participants=providers,
|
||||
)
|
||||
return self.blockchain.FAKE_RECEIPT
|
||||
|
@ -128,7 +155,7 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
participant.aggregated = True
|
||||
participant.decryption_request_static_key = bytes(participant_public_key)
|
||||
|
||||
g1_point = self.Ritual.G1Point.from_dkg_public_key(public_key)
|
||||
g1_point = self.G1Point.from_dkg_public_key(public_key)
|
||||
if len(ritual.aggregated_transcript) == 0:
|
||||
ritual.aggregated_transcript = bytes(aggregated_transcript)
|
||||
ritual.public_key = g1_point
|
||||
|
@ -143,6 +170,33 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
ritual.total_aggregations += 1
|
||||
return self.blockchain.FAKE_RECEIPT
|
||||
|
||||
@staticmethod
|
||||
def is_provider_public_key_set(staking_provider: ChecksumAddress) -> bool:
|
||||
return False
|
||||
|
||||
def set_provider_public_key(
|
||||
self, public_key: FerveoPublicKey, transacting_power: TransactingPower
|
||||
) -> TxReceipt:
|
||||
operator_address = transacting_power.account
|
||||
# either mapping is populated or just assume provider same as operator for testing
|
||||
provider_address = (
|
||||
self._get_staking_provider_from_operator(operator=operator_address)
|
||||
or transacting_power.account
|
||||
)
|
||||
|
||||
participant_keys = self._participant_keys_history.get(provider_address)
|
||||
if not participant_keys:
|
||||
participant_keys = []
|
||||
|
||||
participant_keys.append(
|
||||
self.ParticipantKey(
|
||||
lastRitualId=len(self.rituals),
|
||||
publicKey=self.G2Point.from_public_key(public_key),
|
||||
)
|
||||
)
|
||||
|
||||
return self.blockchain.FAKE_RECEIPT
|
||||
|
||||
#
|
||||
# Calls
|
||||
#
|
||||
|
@ -158,12 +212,12 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
) -> CoordinatorAgent.Ritual:
|
||||
return self.rituals[ritual_id]
|
||||
|
||||
def get_participants(self, ritual_id: int) -> List[Ritual.Participant]:
|
||||
def get_participants(self, ritual_id: int) -> List[Participant]:
|
||||
return self.rituals[ritual_id].participants
|
||||
|
||||
def get_participant_from_provider(
|
||||
self, ritual_id: int, provider: ChecksumAddress
|
||||
) -> Ritual.Participant:
|
||||
) -> Participant:
|
||||
for p in self.rituals[ritual_id].participants:
|
||||
if p.provider == provider:
|
||||
return p
|
||||
|
@ -189,6 +243,15 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
else:
|
||||
raise RuntimeError(f"Ritual {ritual_id} is in an unknown state") # :-(
|
||||
|
||||
def get_ritual_id_from_public_key(self, public_key: DkgPublicKey) -> int:
|
||||
for i, ritual in enumerate(self.rituals):
|
||||
if bytes(ritual.public_key) == bytes(public_key):
|
||||
return i
|
||||
|
||||
raise ValueError(
|
||||
f"No ritual id found for public key 0x{bytes(public_key).hex()}"
|
||||
)
|
||||
|
||||
def get_ritual_public_key(self, ritual_id: int) -> DkgPublicKey:
|
||||
if self.get_ritual_status(ritual_id=ritual_id) != self.RitualStatus.FINALIZED:
|
||||
# TODO should we raise here instead?
|
||||
|
@ -200,8 +263,21 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
|
||||
return ritual.public_key.to_dkg_public_key()
|
||||
|
||||
def get_provider_public_key(
|
||||
self, provider: ChecksumAddress, ritual_id: int
|
||||
) -> FerveoPublicKey:
|
||||
participant_keys = self._participant_keys_history.get(provider)
|
||||
for participant_key in reversed(participant_keys):
|
||||
if participant_key.lastRitualId <= ritual_id:
|
||||
g2Point = participant_key.publicKey
|
||||
return g2Point.to_public_key()
|
||||
|
||||
raise ValueError(
|
||||
f"Public key not found for provider {provider} for ritual #{ritual_id}"
|
||||
)
|
||||
|
||||
def is_encryption_authorized(
|
||||
self, ritual_id: int, evidence: bytes, digest: bytes
|
||||
self, ritual_id: int, evidence: bytes, ciphertext_header: bytes
|
||||
) -> bool:
|
||||
# get ritual -> get access controller -> call isAuthorized(ritualId, evidence, digest)
|
||||
# always allow
|
||||
return True
|
||||
|
|
|
@ -7,7 +7,7 @@ from typing import Union
|
|||
|
||||
from hexbytes import HexBytes
|
||||
|
||||
from nucypher.blockchain.eth.clients import EthereumClient
|
||||
from nucypher.blockchain.eth.clients import EthereumTesterClient
|
||||
from nucypher.blockchain.eth.networks import NetworksInventory
|
||||
from nucypher.blockchain.eth.registry import (
|
||||
BaseContractRegistry,
|
||||
|
@ -82,7 +82,7 @@ class MockBlockchain(TesterBlockchain):
|
|||
return self.FAKE_RECEIPT
|
||||
|
||||
|
||||
class MockEthereumClient(EthereumClient):
|
||||
class MockEthereumClient(EthereumTesterClient):
|
||||
|
||||
def __init__(self, w3):
|
||||
super().__init__(w3=w3, node_technology=None, version=None, platform=None, backend=None)
|
||||
|
|
|
@ -31,12 +31,21 @@ def test_mock_coordinator_creation(coordinator):
|
|||
assert len(coordinator.rituals) == 0
|
||||
|
||||
|
||||
def test_mock_coordinator_initiation(mocker, nodes_transacting_powers, coordinator, random_address):
|
||||
def test_mock_coordinator_initiation(
|
||||
mocker,
|
||||
nodes_transacting_powers,
|
||||
coordinator,
|
||||
random_address,
|
||||
get_random_checksum_address,
|
||||
):
|
||||
assert len(coordinator.rituals) == 0
|
||||
mock_transacting_power = mocker.Mock()
|
||||
mock_transacting_power.account = random_address
|
||||
coordinator.initiate_ritual(
|
||||
providers=list(nodes_transacting_powers.keys()),
|
||||
authority=mock_transacting_power.account,
|
||||
duration=1,
|
||||
access_controller=get_random_checksum_address(),
|
||||
transacting_power=mock_transacting_power,
|
||||
)
|
||||
assert len(coordinator.rituals) == 1
|
||||
|
@ -54,7 +63,7 @@ def test_mock_coordinator_initiation(mocker, nodes_transacting_powers, coordinat
|
|||
signal_type, signal_data = signal
|
||||
assert signal_type == MockCoordinatorAgent.Events.START_RITUAL
|
||||
assert signal_data["ritual_id"] == 0
|
||||
assert signal_data["initiator"] == mock_transacting_power.account
|
||||
assert signal_data["authority"] == mock_transacting_power.account
|
||||
assert set(signal_data["participants"]) == nodes_transacting_powers.keys()
|
||||
|
||||
|
||||
|
|
|
@ -32,9 +32,19 @@ def transacting_power(testerchain, alice):
|
|||
)
|
||||
|
||||
|
||||
def test_initiate_ritual(agent: CoordinatorAgent, cohort, transacting_power):
|
||||
def test_initiate_ritual(
|
||||
agent: CoordinatorAgent, cohort, transacting_power, get_random_checksum_address
|
||||
):
|
||||
# any value will do
|
||||
global_allow_list = get_random_checksum_address()
|
||||
|
||||
duration = 100
|
||||
receipt = agent.initiate_ritual(
|
||||
providers=cohort, transacting_power=transacting_power
|
||||
authority=transacting_power.account,
|
||||
access_controller=global_allow_list,
|
||||
providers=cohort,
|
||||
duration=duration,
|
||||
transacting_power=transacting_power,
|
||||
)
|
||||
|
||||
participants = [
|
||||
|
@ -44,10 +54,16 @@ def test_initiate_ritual(agent: CoordinatorAgent, cohort, transacting_power):
|
|||
for c in cohort
|
||||
]
|
||||
|
||||
init_timestamp = 123456
|
||||
end_timestamp = init_timestamp + duration
|
||||
ritual = CoordinatorAgent.Ritual(
|
||||
initiator=transacting_power.account,
|
||||
authority=transacting_power.account,
|
||||
access_controller=global_allow_list,
|
||||
dkg_size=4,
|
||||
threshold=MockCoordinatorAgent.get_threshold_for_ritual_size(dkg_size=4),
|
||||
init_timestamp=123456,
|
||||
end_timestamp=end_timestamp,
|
||||
participants=participants,
|
||||
)
|
||||
agent.get_ritual = lambda *args, **kwargs: ritual
|
||||
|
@ -59,17 +75,30 @@ def test_initiate_ritual(agent: CoordinatorAgent, cohort, transacting_power):
|
|||
return ritual_id
|
||||
|
||||
|
||||
def test_perform_round_1(ursula, random_address, cohort, agent, random_transcript):
|
||||
def test_perform_round_1(
|
||||
ursula,
|
||||
random_address,
|
||||
cohort,
|
||||
agent,
|
||||
random_transcript,
|
||||
get_random_checksum_address,
|
||||
):
|
||||
participants = dict()
|
||||
for checksum_address in cohort:
|
||||
participants[checksum_address] = CoordinatorAgent.Ritual.Participant(
|
||||
provider=checksum_address,
|
||||
)
|
||||
|
||||
init_timestamp = 123456
|
||||
end_timestamp = init_timestamp + 100
|
||||
ritual = CoordinatorAgent.Ritual(
|
||||
initiator=random_address,
|
||||
authority=random_address,
|
||||
access_controller=get_random_checksum_address(),
|
||||
dkg_size=4,
|
||||
init_timestamp=123456,
|
||||
threshold=MockCoordinatorAgent.get_threshold_for_ritual_size(dkg_size=4),
|
||||
init_timestamp=init_timestamp,
|
||||
end_timestamp=end_timestamp,
|
||||
total_transcripts=4,
|
||||
participants=list(participants.values()),
|
||||
)
|
||||
|
@ -90,7 +119,7 @@ def test_perform_round_1(ursula, random_address, cohort, agent, random_transcrip
|
|||
for state in non_application_states:
|
||||
agent.get_ritual_status = lambda *args, **kwargs: state
|
||||
tx_hash = ursula.perform_round_1(
|
||||
ritual_id=0, initiator=random_address, participants=cohort, timestamp=0
|
||||
ritual_id=0, authority=random_address, participants=cohort, timestamp=0
|
||||
)
|
||||
assert tx_hash is None # no execution performed
|
||||
|
||||
|
@ -100,7 +129,7 @@ def test_perform_round_1(ursula, random_address, cohort, agent, random_transcrip
|
|||
)
|
||||
|
||||
tx_hash = ursula.perform_round_1(
|
||||
ritual_id=0, initiator=random_address, participants=cohort, timestamp=0
|
||||
ritual_id=0, authority=random_address, participants=cohort, timestamp=0
|
||||
)
|
||||
assert tx_hash is not None
|
||||
|
||||
|
@ -109,7 +138,7 @@ def test_perform_round_1(ursula, random_address, cohort, agent, random_transcrip
|
|||
|
||||
# try again
|
||||
tx_hash = ursula.perform_round_1(
|
||||
ritual_id=0, initiator=random_address, participants=cohort, timestamp=0
|
||||
ritual_id=0, authority=random_address, participants=cohort, timestamp=0
|
||||
)
|
||||
assert tx_hash is None # no execution since pending tx already present
|
||||
|
||||
|
@ -124,20 +153,26 @@ def test_perform_round_1(ursula, random_address, cohort, agent, random_transcrip
|
|||
|
||||
# try submitting again
|
||||
tx_hash = ursula.perform_round_1(
|
||||
ritual_id=0, initiator=random_address, participants=cohort, timestamp=0
|
||||
ritual_id=0, authority=random_address, participants=cohort, timestamp=0
|
||||
)
|
||||
assert tx_hash is None # no execution performed
|
||||
|
||||
# participant no longer already posted aggregated transcript
|
||||
participant.transcript = bytes()
|
||||
tx_hash = ursula.perform_round_1(
|
||||
ritual_id=0, initiator=random_address, participants=cohort, timestamp=0
|
||||
ritual_id=0, authority=random_address, participants=cohort, timestamp=0
|
||||
)
|
||||
assert tx_hash is not None # execution occurs
|
||||
|
||||
|
||||
def test_perform_round_2(
|
||||
ursula, cohort, transacting_power, agent, mocker, random_transcript
|
||||
ursula,
|
||||
cohort,
|
||||
transacting_power,
|
||||
agent,
|
||||
mocker,
|
||||
random_transcript,
|
||||
get_random_checksum_address,
|
||||
):
|
||||
participants = dict()
|
||||
for checksum_address in cohort:
|
||||
|
@ -148,10 +183,19 @@ def test_perform_round_2(
|
|||
|
||||
participants[checksum_address] = participant
|
||||
|
||||
init_timestamp = 123456
|
||||
end_timestamp = init_timestamp + 100
|
||||
|
||||
ritual = CoordinatorAgent.Ritual(
|
||||
initiator=transacting_power.account,
|
||||
authority=transacting_power.account,
|
||||
access_controller=get_random_checksum_address(),
|
||||
dkg_size=len(cohort),
|
||||
init_timestamp=123456,
|
||||
threshold=MockCoordinatorAgent.get_threshold_for_ritual_size(
|
||||
dkg_size=len(cohort)
|
||||
),
|
||||
init_timestamp=init_timestamp,
|
||||
end_timestamp=end_timestamp,
|
||||
total_transcripts=len(cohort),
|
||||
participants=list(participants.values()),
|
||||
)
|
||||
|
|
|
@ -1,59 +1,140 @@
|
|||
import json
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Tuple
|
||||
from typing import Dict, List, Tuple, Union
|
||||
|
||||
from ape import config as ape_config
|
||||
from ape import project
|
||||
from ape.api import AccountAPI, DependencyAPI
|
||||
from ape.contracts.base import ContractInstance
|
||||
from ape_test.accounts import TestAccount
|
||||
from eth_typing import ChecksumAddress
|
||||
from eth_utils import to_checksum_address
|
||||
|
||||
from nucypher.blockchain.eth.agents import (
|
||||
CoordinatorAgent,
|
||||
NucypherTokenAgent,
|
||||
PREApplicationAgent,
|
||||
SubscriptionManagerAgent,
|
||||
TACoApplicationAgent,
|
||||
)
|
||||
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
|
||||
from tests.constants import MOCK_STAKING_CONTRACT_NAME
|
||||
from tests.constants import (
|
||||
CONDITION_NFT,
|
||||
GLOBAL_ALLOW_LIST,
|
||||
MOCK_STAKING_CONTRACT_NAME,
|
||||
RITUAL_TOKEN,
|
||||
STAKE_INFO,
|
||||
T_TOKEN,
|
||||
)
|
||||
|
||||
# order sensitive
|
||||
_CONTRACTS_TO_DEPLOY_ON_TESTERCHAIN = (
|
||||
RITUAL_TOKEN,
|
||||
T_TOKEN,
|
||||
NucypherTokenAgent.contract_name,
|
||||
STAKE_INFO,
|
||||
MOCK_STAKING_CONTRACT_NAME,
|
||||
PREApplicationAgent.contract_name,
|
||||
TACoApplicationAgent.contract_name,
|
||||
SubscriptionManagerAgent.contract_name,
|
||||
CoordinatorAgent.contract_name,
|
||||
GLOBAL_ALLOW_LIST,
|
||||
CONDITION_NFT,
|
||||
)
|
||||
|
||||
VARIABLE_PREFIX_SYMBOL = "<"
|
||||
VARIABLE_SUFFIX_SYMBOL = ">"
|
||||
|
||||
|
||||
def get_ape_project_build_path(project) -> Path:
|
||||
build_path = Path(project.path) / '.build'
|
||||
return build_path
|
||||
|
||||
|
||||
def _is_variable(param: Union[str, int, List[Union[str, int]]]) -> bool:
|
||||
"""Check if param is a ape-config variable"""
|
||||
return isinstance(param, str) and (
|
||||
param.startswith(VARIABLE_PREFIX_SYMBOL)
|
||||
and param.endswith(VARIABLE_SUFFIX_SYMBOL)
|
||||
)
|
||||
|
||||
|
||||
def _resolve_variable(
|
||||
param: str,
|
||||
contract_name: str,
|
||||
deployments: Dict[str, ContractInstance],
|
||||
accounts: List[TestAccount],
|
||||
) -> Union[ChecksumAddress, str, int]:
|
||||
"""Resolve a ape-config variable to a literal"""
|
||||
dependency_expression = param.strip(VARIABLE_PREFIX_SYMBOL).strip(
|
||||
VARIABLE_SUFFIX_SYMBOL
|
||||
)
|
||||
dependency_name, attribute_name = dependency_expression.split(".")
|
||||
if dependency_name == "address":
|
||||
try:
|
||||
account = accounts[int(attribute_name)]
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
f"Ape account must be accessed by an index; got '{attribute_name}'."
|
||||
)
|
||||
address = ChecksumAddress(account.address)
|
||||
return address
|
||||
try:
|
||||
param = getattr(deployments[dependency_name], attribute_name)
|
||||
except KeyError:
|
||||
raise ValueError(f"Contract {contract_name} not found in deployments")
|
||||
except AttributeError:
|
||||
raise ValueError(f"Attribute {attribute_name} not found in {dependency_name}")
|
||||
return param
|
||||
|
||||
|
||||
def process_deployment_params(
|
||||
contract_name, params, deployments, symbol: str = "::"
|
||||
) -> Dict[str, Any]:
|
||||
contract_name: str,
|
||||
params: Dict[str, Union[str, int, list]],
|
||||
deployments: Dict[str, ContractInstance],
|
||||
accounts: List[TestAccount],
|
||||
) -> Dict[str, Union[ChecksumAddress, str, int]]:
|
||||
"""Process deployment params for a contract."""
|
||||
processed_params = dict()
|
||||
for k, v in params.items():
|
||||
if isinstance(v, str) and (v.startswith(symbol) and v.endswith(symbol)):
|
||||
dependency_expression = v.strip(symbol)
|
||||
dependency_name, attribute_name = dependency_expression.split(".")
|
||||
try:
|
||||
v = getattr(deployments[dependency_name], attribute_name)
|
||||
except KeyError:
|
||||
raise ValueError(f"Contract {contract_name} not found in deployments")
|
||||
except AttributeError:
|
||||
raise ValueError(
|
||||
f"Attribute {attribute_name} not found in {dependency_name}"
|
||||
)
|
||||
processed_params[k] = v
|
||||
for param_name, param_value in params.items():
|
||||
if _is_variable(param_value):
|
||||
param_value = _resolve_variable(
|
||||
param=param_value,
|
||||
contract_name=contract_name,
|
||||
deployments=deployments,
|
||||
accounts=accounts,
|
||||
)
|
||||
processed_params[param_name] = param_value
|
||||
continue
|
||||
|
||||
elif isinstance(param_value, list):
|
||||
value_list = list()
|
||||
for param in param_value:
|
||||
if _is_variable(param):
|
||||
param = _resolve_variable(
|
||||
param=param,
|
||||
contract_name=contract_name,
|
||||
deployments=deployments,
|
||||
accounts=accounts,
|
||||
)
|
||||
|
||||
value_list.append(param)
|
||||
|
||||
processed_params[param_name] = value_list
|
||||
continue
|
||||
|
||||
else:
|
||||
# this parameter is a literal
|
||||
processed_params[param_name] = param_value
|
||||
continue
|
||||
|
||||
return processed_params
|
||||
|
||||
|
||||
def get_deployment_params(
|
||||
contract_name, config, accounts, deployments
|
||||
contract_name: str,
|
||||
config: Dict[str, Union[str, list]],
|
||||
accounts: List[TestAccount],
|
||||
deployments: Dict[str, ContractInstance],
|
||||
) -> Tuple[Dict, AccountAPI]:
|
||||
"""
|
||||
Get deployment params for a contract.
|
||||
|
@ -64,14 +145,23 @@ def get_deployment_params(
|
|||
deployer_address = accounts[params.pop("address")]
|
||||
name = params.pop("contract_type")
|
||||
if name == contract_name:
|
||||
params = process_deployment_params(contract_name, params, deployments)
|
||||
params = process_deployment_params(
|
||||
contract_name=contract_name,
|
||||
params=params,
|
||||
deployments=deployments,
|
||||
accounts=accounts,
|
||||
)
|
||||
return params, deployer_address
|
||||
else:
|
||||
# there are no deployment params for this contract; default to account at index 0
|
||||
return dict(), accounts[0]
|
||||
|
||||
|
||||
def deploy_contracts(nucypher_contracts: DependencyAPI, accounts):
|
||||
def deploy_contracts(
|
||||
nucypher_contracts: DependencyAPI,
|
||||
test_contracts: DependencyAPI,
|
||||
accounts: List[TestAccount],
|
||||
):
|
||||
"""Deploy contracts o via ape's API for testing."""
|
||||
config = ape_config.get_config("deployments")["ethereum"]["local"]
|
||||
deployments = dict()
|
||||
|
@ -79,22 +169,42 @@ def deploy_contracts(nucypher_contracts: DependencyAPI, accounts):
|
|||
params, deployer_account = get_deployment_params(
|
||||
name, deployments=deployments, config=config, accounts=accounts
|
||||
)
|
||||
dependency_contract = getattr(nucypher_contracts, name)
|
||||
deployed_contract = deployer_account.deploy(dependency_contract, *params.values())
|
||||
try:
|
||||
# this contract is a dependency
|
||||
contract = getattr(nucypher_contracts, name)
|
||||
except AttributeError:
|
||||
# this contract is local to this project
|
||||
try:
|
||||
contract = getattr(project, name)
|
||||
except AttributeError:
|
||||
raise ValueError(
|
||||
f"Contract {name} not found in project or in dependencies."
|
||||
)
|
||||
deployed_contract = deployer_account.deploy(contract, *params.values())
|
||||
deployments[name] = deployed_contract
|
||||
return deployments
|
||||
|
||||
|
||||
def registry_from_ape_deployments(nucypher_contracts: DependencyAPI, deployments: Dict) -> InMemoryContractRegistry:
|
||||
def registry_from_ape_deployments(
|
||||
nucypher_contracts: DependencyAPI, deployments: Dict[str, ContractInstance]
|
||||
) -> InMemoryContractRegistry:
|
||||
"""Creates a registry from ape deployments."""
|
||||
|
||||
# Get the raw abi from the cached manifest
|
||||
manifest = json.loads(nucypher_contracts.cached_manifest.json())
|
||||
contract_data = manifest['contractTypes']
|
||||
local_contracts = project.contracts
|
||||
|
||||
# Get the raw abi from the cached dependency manifest
|
||||
dependency_manifest = json.loads(nucypher_contracts.cached_manifest.json())
|
||||
combined_contract_data = dependency_manifest["contractTypes"]
|
||||
|
||||
# Add the local contract ABIs to the data
|
||||
for contract_name, local_contract_data in local_contracts.items():
|
||||
contract_manifest = json.loads(local_contract_data.json())
|
||||
contract_abi = contract_manifest["abi"]
|
||||
combined_contract_data[contract_name] = {"abi": contract_abi}
|
||||
|
||||
data = list()
|
||||
for contract_name, deployment in deployments.items():
|
||||
abi = contract_data[contract_name]['abi']
|
||||
abi = combined_contract_data[contract_name]["abi"]
|
||||
entry = [
|
||||
contract_name,
|
||||
'v0.0.0', # TODO: get version from contract
|
||||
|
|
|
@ -1,25 +1,17 @@
|
|||
import os
|
||||
from typing import List, Optional, Tuple, Union
|
||||
from typing import List, Union
|
||||
|
||||
import maya
|
||||
from ape_test import LocalProvider
|
||||
from eth_tester.exceptions import TransactionFailed
|
||||
from eth_utils import to_canonical_address
|
||||
from hexbytes import HexBytes
|
||||
from web3 import Web3
|
||||
|
||||
from nucypher.blockchain.economics import Economics
|
||||
from nucypher.blockchain.eth.interfaces import (
|
||||
BlockchainInterface,
|
||||
BlockchainInterfaceFactory,
|
||||
)
|
||||
from nucypher.blockchain.eth.registry import (
|
||||
BaseContractRegistry,
|
||||
InMemoryContractRegistry,
|
||||
)
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.blockchain.eth.token import NU
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.utilities.gas_strategies import EXPECTED_CONFIRMATION_TIME_IN_SECONDS
|
||||
from nucypher.utilities.logging import Logger
|
||||
|
|
Loading…
Reference in New Issue