mirror of https://github.com/nucypher/nucypher.git
Deployer tooling for accessing untarget, but enrolled contracts by version index.
parent
859e572587
commit
2567bdd3ec
|
@ -165,6 +165,13 @@ class BaseContractDeployer:
|
|||
agent = self.agency(registry=self.registry, contract=self._contract)
|
||||
return agent
|
||||
|
||||
def get_latest_enrollment(self, registry: BaseContractRegistry) -> Contract:
|
||||
"""Get the latest enrolled version of the contract from the registry."""
|
||||
contract = self.blockchain.get_contract_by_name(name=self.contract_name,
|
||||
registry=registry,
|
||||
use_proxy_address=False,
|
||||
version='latest')
|
||||
return contract
|
||||
|
||||
class OwnableContractMixin:
|
||||
|
||||
|
@ -221,15 +228,17 @@ class UpgradeableContractMixin:
|
|||
raise self.ContractNotUpgradeable(f"{self.contract_name} is not upgradeable.")
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def get_latest_version(cls, registry: BaseContractRegistry, provider_uri: str = None) -> Contract:
|
||||
"""Get the latest version of the contract without assembling it with it's proxy."""
|
||||
if not cls._upgradeable:
|
||||
raise cls.ContractNotUpgradeable(f"{cls.contract_name} is not upgradeable.")
|
||||
def get_principal_contract(self, registry: BaseContractRegistry, provider_uri: str = None) -> Contract:
|
||||
"""
|
||||
Get the on-chain targeted version of the principal contract directly
|
||||
without assembling it with it's proxy.
|
||||
"""
|
||||
if not self._upgradeable:
|
||||
raise cls.ContractNotUpgradeable(f"{self.contract_name} is not upgradeable.")
|
||||
blockchain = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri)
|
||||
contract = blockchain.get_contract_by_name(name=cls.contract_name,
|
||||
contract = blockchain.get_contract_by_name(name=self.contract_name,
|
||||
registry=registry,
|
||||
proxy_name=cls._proxy_deployer.contract_name,
|
||||
proxy_name=self._proxy_deployer.contract_name,
|
||||
use_proxy_address=False)
|
||||
return contract
|
||||
|
||||
|
@ -247,8 +256,8 @@ class UpgradeableContractMixin:
|
|||
raise self.ContractNotUpgradeable(f"{self.contract_name} is not upgradeable.")
|
||||
|
||||
# 1 - Get Bare Contracts
|
||||
existing_bare_contract = self.get_latest_version(registry=self.registry,
|
||||
provider_uri=self.blockchain.provider_uri)
|
||||
existing_bare_contract = self.get_principal_contract(registry=self.registry,
|
||||
provider_uri=self.blockchain.provider_uri)
|
||||
|
||||
proxy_deployer = self._proxy_deployer(registry=self.registry,
|
||||
target_contract=existing_bare_contract,
|
||||
|
@ -275,8 +284,8 @@ class UpgradeableContractMixin:
|
|||
self.check_deployment_readiness()
|
||||
|
||||
# 2 - Get Bare Contracts
|
||||
existing_bare_contract = self.get_latest_version(registry=self.registry,
|
||||
provider_uri=self.blockchain.provider_uri)
|
||||
existing_bare_contract = self.get_principal_contract(registry=self.registry,
|
||||
provider_uri=self.blockchain.provider_uri)
|
||||
|
||||
proxy_deployer = self._proxy_deployer(registry=self.registry,
|
||||
target_contract=existing_bare_contract,
|
||||
|
|
|
@ -425,6 +425,7 @@ class BlockchainInterface:
|
|||
def get_contract_by_name(self,
|
||||
registry: BaseContractRegistry,
|
||||
name: str,
|
||||
version: int = None,
|
||||
proxy_name: str = None,
|
||||
use_proxy_address: bool = True
|
||||
) -> Union[Contract, List[tuple]]:
|
||||
|
@ -438,9 +439,9 @@ class BlockchainInterface:
|
|||
if not target_contract_records:
|
||||
raise self.UnknownContract(f"No such contract records with name {name}.")
|
||||
|
||||
if proxy_name: # It's upgradeable
|
||||
# Lookup proxies; Search for a published proxy that targets this contract record
|
||||
if proxy_name:
|
||||
|
||||
# Lookup proxies; Search for a published proxy that targets this contract record
|
||||
proxy_records = registry.search(contract_name=proxy_name)
|
||||
|
||||
results = list()
|
||||
|
@ -474,11 +475,21 @@ class BlockchainInterface:
|
|||
except IndexError:
|
||||
raise self.UnknownContract(f"There are no Dispatcher records targeting '{name}'")
|
||||
|
||||
else: # It's not upgradeable
|
||||
else:
|
||||
# NOTE: 0 must be allowed as a valid version number
|
||||
if len(target_contract_records) != 1:
|
||||
m = "Multiple records registered for non-upgradeable contract {}"
|
||||
raise self.InterfaceError(m.format(name))
|
||||
_target_contract_name, selected_address, selected_abi = target_contract_records[0]
|
||||
if version is None:
|
||||
m = f"{len(target_contract_records)} records enrolled for contract {name} " \
|
||||
f"and no version index was supplied."
|
||||
raise self.InterfaceError(m.format(name))
|
||||
version = self.__get_version_index(name=name,
|
||||
version_index=version,
|
||||
enrollments=len(target_contract_records))
|
||||
|
||||
else:
|
||||
version = -1 # default
|
||||
|
||||
_target_contract_name, selected_address, selected_abi = target_contract_records[version]
|
||||
|
||||
# Create the contract from selected sources
|
||||
unified_contract = self.client.w3.eth.contract(abi=selected_abi,
|
||||
|
@ -487,6 +498,23 @@ class BlockchainInterface:
|
|||
|
||||
return unified_contract
|
||||
|
||||
@staticmethod
|
||||
def __get_version_index(version_index: Union[int, str], enrollments: int, name: str):
|
||||
version_names = {'latest': -1, 'earliest': 0}
|
||||
try:
|
||||
version = version_names[version_index]
|
||||
except KeyError:
|
||||
try:
|
||||
version = int(version_index)
|
||||
except ValueError:
|
||||
what_is_this = version_index
|
||||
raise ValueError(f"'{what_is_this}' is a valid version number")
|
||||
else:
|
||||
if version > enrollments - 1:
|
||||
message = f"Version index '{version}' is larger than the number of enrollments for {name}."
|
||||
raise ValueError(message)
|
||||
return version
|
||||
|
||||
|
||||
class BlockchainDeployerInterface(BlockchainInterface):
|
||||
|
||||
|
|
|
@ -22,11 +22,12 @@ import tempfile
|
|||
from abc import ABC, abstractmethod
|
||||
from json import JSONDecodeError
|
||||
from os.path import dirname, abspath
|
||||
from typing import Union
|
||||
from typing import Union, Iterator
|
||||
|
||||
import requests
|
||||
from constant_sorrow.constants import REGISTRY_COMMITTED
|
||||
from twisted.logger import Logger
|
||||
from web3.contract import Contract
|
||||
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||
|
||||
|
@ -129,16 +130,16 @@ class BaseContractRegistry(ABC):
|
|||
return instance
|
||||
|
||||
@property
|
||||
def enrolled_names(self):
|
||||
def enrolled_names(self) -> Iterator:
|
||||
entries = iter(record[0] for record in self.read())
|
||||
return entries
|
||||
|
||||
@property
|
||||
def enrolled_addresses(self):
|
||||
def enrolled_addresses(self) -> Iterator:
|
||||
entries = iter(record[1] for record in self.read())
|
||||
return entries
|
||||
|
||||
def enroll(self, contract_name, contract_address, contract_abi):
|
||||
def enroll(self, contract_name, contract_address, contract_abi) -> None:
|
||||
"""
|
||||
Enrolls a contract to the chain registry by writing the name, address,
|
||||
and abi information to the filesystem as JSON.
|
||||
|
@ -157,7 +158,7 @@ class BaseContractRegistry(ABC):
|
|||
self.write(registry_data)
|
||||
self.log.info("Enrolled {}:{} into registry.".format(contract_name, contract_address))
|
||||
|
||||
def search(self, contract_name: str = None, contract_address: str = None):
|
||||
def search(self, contract_name: str = None, contract_address: str = None) -> tuple:
|
||||
"""
|
||||
Searches the registry for a contract with the provided name or address
|
||||
and returns the contracts component data.
|
||||
|
@ -185,7 +186,8 @@ class BaseContractRegistry(ABC):
|
|||
self.log.critical(m)
|
||||
raise self.IllegalRegistry(m.format(contract_address))
|
||||
|
||||
return contracts if contract_name else contracts[0]
|
||||
result = tuple(contracts) if contract_name else contracts[0]
|
||||
return result
|
||||
|
||||
|
||||
class LocalContractRegistry(BaseContractRegistry):
|
||||
|
|
|
@ -155,7 +155,7 @@ def test_deploy_bare_upgradeable_contract_deployment(testerchain, test_registry,
|
|||
new_number_of_enrollments = enrolled_names.count(StakingEscrowDeployer.contract_name)
|
||||
new_number_of_proxy_enrollments = enrolled_names.count(StakingEscrowDeployer._proxy_deployer.contract_name)
|
||||
|
||||
# The prinicipal contract was deployed.
|
||||
# The principal contract was deployed.
|
||||
assert new_number_of_enrollments == (old_number_of_enrollments + 1)
|
||||
|
||||
# The Dispatcher was not deployed.
|
||||
|
@ -169,30 +169,31 @@ def test_manual_proxy_retargeting(testerchain, test_registry, token_economics):
|
|||
economics=token_economics)
|
||||
|
||||
# Get Proxy-Direct
|
||||
existing_bare_contract = deployer.get_latest_version(registry=test_registry, provider_uri=TEST_PROVIDER_URI)
|
||||
existing_bare_contract = deployer.get_principal_contract(registry=test_registry, provider_uri=TEST_PROVIDER_URI)
|
||||
proxy_deployer = StakingEscrowDeployer._proxy_deployer(registry=test_registry,
|
||||
target_contract=existing_bare_contract,
|
||||
deployer_address=testerchain.etherbase_account,
|
||||
bare=True) # acquire agency for the proxy itself.
|
||||
|
||||
# Re-Deploy Staking Escrow
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
|
||||
old_target = proxy_deployer.contract.functions.target().call()
|
||||
|
||||
old_secret = bytes("...maybe not.", encoding='utf-8')
|
||||
new_secret = keccak_digest(bytes('thistimeforsure', encoding='utf-8'))
|
||||
receipt = deployer.retarget(target_address=staking_agent.contract_address,
|
||||
|
||||
# Get the latest un-targeted contract from the registry
|
||||
latest_deployment = deployer.get_latest_enrollment(registry=test_registry)
|
||||
receipt = deployer.retarget(target_address=latest_deployment.address,
|
||||
existing_secret_plaintext=old_secret,
|
||||
new_secret_hash=new_secret)
|
||||
|
||||
assert receipt['status'] == 1
|
||||
|
||||
#
|
||||
# Post-Retargeting
|
||||
#
|
||||
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
|
||||
# Check proxy targets
|
||||
new_target = proxy_deployer.contract.functions.target().call()
|
||||
|
||||
assert old_target != new_target
|
||||
assert new_target == staking_agent.contract_address
|
||||
assert new_target == latest_deployment.address
|
||||
|
||||
# Check address consistency
|
||||
new_bare_contract = deployer.get_principal_contract(registry=test_registry, provider_uri=TEST_PROVIDER_URI)
|
||||
assert new_bare_contract.address == latest_deployment.address == new_target
|
||||
|
|
Loading…
Reference in New Issue