mirror of https://github.com/nucypher/nucypher.git
commit
53e1624ced
4
mypy.ini
4
mypy.ini
|
@ -6,3 +6,7 @@ disallow_untyped_defs=False
|
||||||
check_untyped_defs=False
|
check_untyped_defs=False
|
||||||
disallow_untyped_calls=False
|
disallow_untyped_calls=False
|
||||||
ignore_missing_imports=True
|
ignore_missing_imports=True
|
||||||
|
warn_return_any = False
|
||||||
|
no_implicit_optional = False
|
||||||
|
strict_optional = False
|
||||||
|
warn_no_return = False
|
|
@ -1,6 +1,6 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Tuple
|
from typing import Tuple, List
|
||||||
|
|
||||||
import maya
|
import maya
|
||||||
|
|
||||||
|
@ -37,18 +37,18 @@ class NucypherTokenActor:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
parent_address = self.checksum_public_address
|
parent_address = self.checksum_public_address # type: str
|
||||||
if checksum_address is not None:
|
if checksum_address is not None:
|
||||||
if parent_address != checksum_address:
|
if parent_address != checksum_address:
|
||||||
raise ValueError("Can't have two different addresses.")
|
raise ValueError("Can't have two different addresses.")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self.checksum_public_address = checksum_address
|
self.checksum_public_address = checksum_address # type: str
|
||||||
|
|
||||||
if registry_filepath is not None:
|
if registry_filepath is not None:
|
||||||
EthereumContractRegistry(registry_filepath=registry_filepath)
|
EthereumContractRegistry(registry_filepath=registry_filepath)
|
||||||
|
|
||||||
self.token_agent = token_agent if token_agent is not None else NucypherTokenAgent()
|
self.token_agent = token_agent if token_agent is not None else NucypherTokenAgent()
|
||||||
self._transaction_cache = list() # track transactions transmitted
|
self._transaction_cache = list() # type: list # track transactions transmitted
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
class_name = self.__class__.__name__
|
class_name = self.__class__.__name__
|
||||||
|
@ -206,7 +206,7 @@ class Miner(NucypherTokenActor):
|
||||||
if entire_balance is True:
|
if entire_balance is True:
|
||||||
amount = self.token_balance
|
amount = self.token_balance
|
||||||
|
|
||||||
staking_transactions = OrderedDict() # Time series of txhases
|
staking_transactions = OrderedDict() # type: OrderedDict # Time series of txhases
|
||||||
|
|
||||||
# Validate
|
# Validate
|
||||||
assert self.__validate_stake(amount=amount, lock_periods=lock_periods)
|
assert self.__validate_stake(amount=amount, lock_periods=lock_periods)
|
||||||
|
@ -281,7 +281,7 @@ class PolicyAuthor(NucypherTokenActor):
|
||||||
checksum_address=checksum_address,
|
checksum_address=checksum_address,
|
||||||
)
|
)
|
||||||
|
|
||||||
def recruit(self, quantity: int, **options) -> None:
|
def recruit(self, quantity: int, **options) -> List[str]:
|
||||||
"""
|
"""
|
||||||
Uses sampling logic to gather miners from the blockchain and
|
Uses sampling logic to gather miners from the blockchain and
|
||||||
caches the resulting node ethereum addresses.
|
caches the resulting node ethereum addresses.
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import random
|
import random
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from typing import Generator, List, Tuple, Union
|
|
||||||
|
|
||||||
from constant_sorrow import constants
|
from constant_sorrow import constants
|
||||||
|
from typing import Generator, List, Tuple, Union
|
||||||
|
from web3.contract import Contract
|
||||||
|
|
||||||
from nucypher.blockchain.eth import constants
|
from nucypher.blockchain.eth import constants
|
||||||
from nucypher.blockchain.eth.chains import Blockchain
|
from nucypher.blockchain.eth.chains import Blockchain
|
||||||
from nucypher.blockchain.eth.interfaces import EthereumContractRegistry
|
|
||||||
|
|
||||||
|
|
||||||
class EthereumContractAgent(ABC):
|
class EthereumContractAgent(ABC):
|
||||||
|
@ -31,20 +31,21 @@ class EthereumContractAgent(ABC):
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
blockchain: Blockchain = None,
|
blockchain: Blockchain = None,
|
||||||
registry_filepath: str = None,
|
registry_filepath: str = None,
|
||||||
*args, **kwargs) -> None:
|
contract: Contract = None
|
||||||
|
) -> None:
|
||||||
|
|
||||||
if blockchain is None:
|
if blockchain is None:
|
||||||
blockchain = Blockchain.connect()
|
blockchain = Blockchain.connect()
|
||||||
self.blockchain = blockchain
|
self.blockchain = blockchain
|
||||||
|
|
||||||
if registry_filepath is not None:
|
if registry_filepath is not None:
|
||||||
# TODO: Warn on override?
|
# TODO: Warn on override/ do this elsewhere?
|
||||||
self.blockchain.interface._registry._swap_registry(filepath=registry_filepath)
|
self.blockchain.interface._registry._swap_registry(filepath=registry_filepath)
|
||||||
|
|
||||||
# Fetch the contract by reading address and abi from the registry and blockchain
|
if contract is None:
|
||||||
contract = self.blockchain.interface.get_contract_by_name(name=self.principal_contract_name,
|
# Fetch the contract by reading address and abi from the registry and blockchain
|
||||||
upgradeable=self._upgradeable)
|
contract = self.blockchain.interface.get_contract_by_name(name=self.principal_contract_name,
|
||||||
|
upgradeable=self._upgradeable)
|
||||||
self.__contract = contract
|
self.__contract = contract
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
@ -98,14 +99,23 @@ class MinerAgent(EthereumContractAgent):
|
||||||
|
|
||||||
principal_contract_name = "MinersEscrow"
|
principal_contract_name = "MinersEscrow"
|
||||||
_upgradeable = True
|
_upgradeable = True
|
||||||
__instance = None
|
__instance = None # TODO: constants.NO_CONTRACT_AVAILABLE
|
||||||
|
|
||||||
class NotEnoughMiners(Exception):
|
class NotEnoughMiners(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, token_agent: NucypherTokenAgent=None, registry_filepath=None, *args, **kwargs) -> None:
|
def __init__(self,
|
||||||
|
token_agent: NucypherTokenAgent = None,
|
||||||
|
registry_filepath: str = None,
|
||||||
|
*args, **kwargs
|
||||||
|
) -> None:
|
||||||
|
|
||||||
token_agent = token_agent if token_agent is not None else NucypherTokenAgent(registry_filepath=registry_filepath)
|
token_agent = token_agent if token_agent is not None else NucypherTokenAgent(registry_filepath=registry_filepath)
|
||||||
super().__init__(blockchain=token_agent.blockchain, registry_filepath=registry_filepath, *args, **kwargs)
|
|
||||||
|
super().__init__(blockchain=token_agent.blockchain,
|
||||||
|
registry_filepath=registry_filepath,
|
||||||
|
*args, **kwargs)
|
||||||
|
|
||||||
self.token_agent = token_agent
|
self.token_agent = token_agent
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
from constant_sorrow import constants
|
||||||
from typing import Tuple, Dict
|
from typing import Tuple, Dict
|
||||||
|
|
||||||
from constant_sorrow import constants
|
from nucypher.blockchain.eth.agents import (
|
||||||
|
EthereumContractAgent,
|
||||||
from nucypher.blockchain.eth.agents import EthereumContractAgent, MinerAgent, NucypherTokenAgent, PolicyAgent, \
|
MinerAgent,
|
||||||
|
NucypherTokenAgent,
|
||||||
|
PolicyAgent,
|
||||||
UserEscrowAgent
|
UserEscrowAgent
|
||||||
|
)
|
||||||
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface
|
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface
|
||||||
from .chains import Blockchain
|
from .chains import Blockchain
|
||||||
|
|
||||||
|
@ -18,10 +22,14 @@ class ContractDeployer:
|
||||||
class ContractDeploymentError(Exception):
|
class ContractDeploymentError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, blockchain: Blockchain, deployer_address: str) -> None:
|
def __init__(self,
|
||||||
|
blockchain: Blockchain,
|
||||||
|
deployer_address: str
|
||||||
|
) -> None:
|
||||||
|
|
||||||
self.__armed = False
|
self.__armed = False
|
||||||
self._contract = None
|
self._contract = constants.CONTRACT_NOT_DEPLOYED
|
||||||
self.deployment_receipt = None
|
self.deployment_receipt = constants.CONTRACT_NOT_DEPLOYED
|
||||||
self.__dispatcher = NotImplemented
|
self.__dispatcher = NotImplemented
|
||||||
|
|
||||||
# Sanity check
|
# Sanity check
|
||||||
|
@ -33,13 +41,11 @@ class ContractDeployer:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def contract_address(self) -> str:
|
def contract_address(self) -> str:
|
||||||
try:
|
if self._contract is constants.CONTRACT_NOT_DEPLOYED:
|
||||||
address = self._contract.address
|
|
||||||
except AttributeError:
|
|
||||||
cls = self.__class__
|
cls = self.__class__
|
||||||
raise cls.ContractDeploymentError('Contract not deployed')
|
raise ContractDeployer.ContractDeploymentError('Contract not deployed')
|
||||||
else:
|
address = self._contract.address # type: str
|
||||||
return address
|
return address
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def deployer_address(self):
|
def deployer_address(self):
|
||||||
|
@ -55,7 +61,7 @@ class ContractDeployer:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_deployed(self) -> bool:
|
def is_deployed(self) -> bool:
|
||||||
return bool(self._contract is not None)
|
return bool(self._contract is not constants.CONTRACT_NOT_DEPLOYED)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_armed(self) -> bool:
|
def is_armed(self) -> bool:
|
||||||
|
@ -95,14 +101,14 @@ class ContractDeployer:
|
||||||
def _ensure_contract_deployment(self) -> bool:
|
def _ensure_contract_deployment(self) -> bool:
|
||||||
"""Raises ContractDeploymentError if the contract has not been armed and deployed."""
|
"""Raises ContractDeploymentError if the contract has not been armed and deployed."""
|
||||||
|
|
||||||
if self._contract is None:
|
if self._contract is constants.CONTRACT_NOT_DEPLOYED:
|
||||||
class_name = self.__class__.__name__
|
class_name = self.__class__.__name__
|
||||||
message = '{} contract is not deployed. Arm, then deploy.'.format(class_name)
|
message = '{} contract is not deployed. Arm, then deploy.'.format(class_name)
|
||||||
raise self.ContractDeploymentError(message)
|
raise self.ContractDeploymentError(message)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def arm(self, fail_on_abort=True) -> None:
|
def arm(self, fail_on_abort: bool = True) -> None:
|
||||||
"""
|
"""
|
||||||
Safety mechanism for ethereum contract deployment
|
Safety mechanism for ethereum contract deployment
|
||||||
|
|
||||||
|
@ -140,7 +146,7 @@ class ContractDeployer:
|
||||||
arm = True # If this is a private chain, just arm the deployer without interaction.
|
arm = True # If this is a private chain, just arm the deployer without interaction.
|
||||||
self.__armed = arm # Set the arming status
|
self.__armed = arm # Set the arming status
|
||||||
|
|
||||||
def deploy(self) -> str:
|
def deploy(self) -> dict:
|
||||||
"""
|
"""
|
||||||
Used after arming the deployer;
|
Used after arming the deployer;
|
||||||
Provides for the setup, deployment, and initialization of ethereum smart contracts.
|
Provides for the setup, deployment, and initialization of ethereum smart contracts.
|
||||||
|
@ -158,7 +164,11 @@ class NucypherTokenDeployer(ContractDeployer):
|
||||||
agency = NucypherTokenAgent
|
agency = NucypherTokenAgent
|
||||||
_contract_name = agency.principal_contract_name # TODO
|
_contract_name = agency.principal_contract_name # TODO
|
||||||
|
|
||||||
def __init__(self, blockchain, deployer_address) -> None:
|
def __init__(self,
|
||||||
|
blockchain,
|
||||||
|
deployer_address: str
|
||||||
|
) -> None:
|
||||||
|
|
||||||
if not type(blockchain.interface) is self._interface_class:
|
if not type(blockchain.interface) is self._interface_class:
|
||||||
raise ValueError("{} must be used to create a {}".format(self._interface_class.__name__,
|
raise ValueError("{} must be used to create a {}".format(self._interface_class.__name__,
|
||||||
self.__class__.__name__))
|
self.__class__.__name__))
|
||||||
|
@ -166,7 +176,7 @@ class NucypherTokenDeployer(ContractDeployer):
|
||||||
super().__init__(blockchain=blockchain, deployer_address=deployer_address)
|
super().__init__(blockchain=blockchain, deployer_address=deployer_address)
|
||||||
self._creator = deployer_address
|
self._creator = deployer_address
|
||||||
|
|
||||||
def deploy(self) -> str:
|
def deploy(self) -> dict:
|
||||||
"""
|
"""
|
||||||
Deploy and publish the NuCypher Token contract
|
Deploy and publish the NuCypher Token contract
|
||||||
to the blockchain network specified in self.blockchain.network.
|
to the blockchain network specified in self.blockchain.network.
|
||||||
|
@ -183,7 +193,7 @@ class NucypherTokenDeployer(ContractDeployer):
|
||||||
int(constants.TOKEN_SATURATION))
|
int(constants.TOKEN_SATURATION))
|
||||||
|
|
||||||
self._contract = _contract
|
self._contract = _contract
|
||||||
return self.deployment_receipt
|
return {'deployment_receipt': self.deployment_receipt}
|
||||||
|
|
||||||
|
|
||||||
class DispatcherDeployer(ContractDeployer):
|
class DispatcherDeployer(ContractDeployer):
|
||||||
|
@ -199,14 +209,14 @@ class DispatcherDeployer(ContractDeployer):
|
||||||
self.secret_hash = secret_hash
|
self.secret_hash = secret_hash
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def deploy(self) -> str:
|
def deploy(self) -> dict:
|
||||||
|
|
||||||
dispatcher_contract, txhash = self.blockchain.interface.deploy_contract('Dispatcher',
|
dispatcher_contract, txhash = self.blockchain.interface.deploy_contract('Dispatcher',
|
||||||
self.target_contract.address,
|
self.target_contract.address,
|
||||||
self.secret_hash)
|
self.secret_hash)
|
||||||
|
|
||||||
self._contract = dispatcher_contract
|
self._contract = dispatcher_contract
|
||||||
return txhash
|
return {'txhash': txhash}
|
||||||
|
|
||||||
|
|
||||||
class MinerEscrowDeployer(ContractDeployer):
|
class MinerEscrowDeployer(ContractDeployer):
|
||||||
|
@ -227,7 +237,7 @@ class MinerEscrowDeployer(ContractDeployer):
|
||||||
if result is constants.NULL_ADDRESS:
|
if result is constants.NULL_ADDRESS:
|
||||||
raise RuntimeError("PolicyManager contract is not initialized.")
|
raise RuntimeError("PolicyManager contract is not initialized.")
|
||||||
|
|
||||||
def deploy(self) -> Dict[str, str]:
|
def deploy(self) -> dict:
|
||||||
"""
|
"""
|
||||||
Deploy and publish the NuCypher Token contract
|
Deploy and publish the NuCypher Token contract
|
||||||
to the blockchain network specified in self.blockchain.network.
|
to the blockchain network specified in self.blockchain.network.
|
||||||
|
@ -383,7 +393,7 @@ class UserEscrowDeployer(ContractDeployer):
|
||||||
self.token_deployer = miner_escrow_deployer.token_deployer
|
self.token_deployer = miner_escrow_deployer.token_deployer
|
||||||
super().__init__(blockchain=miner_escrow_deployer.blockchain, *args, **kwargs)
|
super().__init__(blockchain=miner_escrow_deployer.blockchain, *args, **kwargs)
|
||||||
|
|
||||||
def deploy(self):
|
def deploy(self) -> dict:
|
||||||
is_ready, _disqualifications = self.check_ready_to_deploy(fail=True)
|
is_ready, _disqualifications = self.check_ready_to_deploy(fail=True)
|
||||||
assert is_ready
|
assert is_ready
|
||||||
|
|
||||||
|
@ -391,11 +401,11 @@ class UserEscrowDeployer(ContractDeployer):
|
||||||
self.miner_deployer.contract_address,
|
self.miner_deployer.contract_address,
|
||||||
self.policy_deployer.contract_address]
|
self.policy_deployer.contract_address]
|
||||||
|
|
||||||
deploy_transaction = {'from': self.token_deployer.contract_address}
|
deploy_transaction = {'from': self.token_deployer.contract_address} # TODO:.. eh?
|
||||||
|
|
||||||
the_user_escrow_contract, deploy_txhash = self.blockchain.interface.deploy_contract(
|
the_user_escrow_contract, deploy_txhash = self.blockchain.interface.deploy_contract(
|
||||||
self._contract_name,
|
self._contract_name,
|
||||||
*deployment_args)
|
*deployment_args)
|
||||||
|
|
||||||
self._contract = the_user_escrow_contract
|
self._contract = the_user_escrow_contract
|
||||||
return deploy_txhash
|
return {'deploy_txhash': deploy_txhash}
|
||||||
|
|
|
@ -155,7 +155,7 @@ class BlockchainInterface:
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_config(cls, config: NodeConfiguration) -> 'BlockchainInterface':
|
def from_config(cls, config: NodeConfiguration) -> 'BlockchainInterface':
|
||||||
# Parse
|
# Parse
|
||||||
payload = parse_blockchain_config(filepath=config.ini_filepath)
|
payload = parse_blockchain_config(filepath=config.config_file_location)
|
||||||
|
|
||||||
# Init deps
|
# Init deps
|
||||||
compiler = SolidityCompiler() if payload['compile'] else None
|
compiler = SolidityCompiler() if payload['compile'] else None
|
||||||
|
@ -171,7 +171,7 @@ class BlockchainInterface:
|
||||||
return circumflex
|
return circumflex
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def providers(self) -> Tuple[Union[IPCProvider, WebsocketProvider, HTTPProvider]]:
|
def providers(self) -> Tuple[Union[IPCProvider, WebsocketProvider, HTTPProvider], ...]:
|
||||||
return tuple(self.__providers)
|
return tuple(self.__providers)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from typing import List
|
from typing import List, Tuple, Iterable
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
@ -9,7 +9,7 @@ from eth_utils import to_canonical_address
|
||||||
|
|
||||||
from nucypher.blockchain.eth.actors import Miner
|
from nucypher.blockchain.eth.actors import Miner
|
||||||
from nucypher.blockchain.eth.actors import PolicyAuthor
|
from nucypher.blockchain.eth.actors import PolicyAuthor
|
||||||
from nucypher.blockchain.eth.agents import MinerAgent
|
from nucypher.blockchain.eth.agents import MinerAgent, PolicyAgent
|
||||||
from nucypher.blockchain.eth.constants import calculate_period_duration
|
from nucypher.blockchain.eth.constants import calculate_period_duration
|
||||||
from nucypher.characters.lawful import Ursula
|
from nucypher.characters.lawful import Ursula
|
||||||
from nucypher.network.middleware import RestMiddleware
|
from nucypher.network.middleware import RestMiddleware
|
||||||
|
@ -28,29 +28,30 @@ class BlockchainArrangement(Arrangement):
|
||||||
lock_periods: int,
|
lock_periods: int,
|
||||||
expiration: maya.MayaDT,
|
expiration: maya.MayaDT,
|
||||||
*args, **kwargs) -> None:
|
*args, **kwargs) -> None:
|
||||||
|
|
||||||
super().__init__(alice=author, ursula=miner, *args, **kwargs)
|
super().__init__(alice=author, ursula=miner, *args, **kwargs)
|
||||||
|
|
||||||
delta = expiration - maya.now()
|
delta = expiration - maya.now()
|
||||||
hours = (delta.total_seconds() / 60) / 60
|
hours = (delta.total_seconds() / 60) / 60 # type: int
|
||||||
periods = int(math.ceil(hours / int(constants.HOURS_PER_PERIOD)))
|
periods = int(math.ceil(hours / int(constants.HOURS_PER_PERIOD))) # type: int
|
||||||
|
|
||||||
# The relationship exists between two addresses
|
# The relationship exists between two addresses
|
||||||
self.author = author
|
self.author = author # type: PolicyAuthor
|
||||||
self.policy_agent = author.policy_agent
|
self.policy_agent = author.policy_agent # type: PolicyAgent
|
||||||
|
|
||||||
self.miner = miner
|
self.miner = miner # type: Miner
|
||||||
|
|
||||||
# Arrangement value, rate, and duration
|
# Arrangement value, rate, and duration
|
||||||
rate = value // lock_periods
|
rate = value // lock_periods # type: int
|
||||||
self._rate = rate
|
self._rate = rate # type: int
|
||||||
|
|
||||||
self.value = value
|
self.value = value # type: int
|
||||||
self.lock_periods = lock_periods # TODO: <datetime> -> lock_periods
|
self.lock_periods = lock_periods # type: int # TODO: <datetime> -> lock_periods
|
||||||
|
|
||||||
self.is_published = False
|
self.is_published = False # type: bool
|
||||||
self.publish_transaction = None
|
self.publish_transaction = None
|
||||||
|
|
||||||
self.is_revoked = False
|
self.is_revoked = False # type: bool
|
||||||
self.revoke_transaction = None
|
self.revoke_transaction = None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -60,9 +61,11 @@ class BlockchainArrangement(Arrangement):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def publish(self) -> str:
|
def publish(self) -> str:
|
||||||
payload = {'from': self.author._ether_address, 'value': self.value}
|
payload = {'from': self.author.checksum_public_address,
|
||||||
|
'value': self.value}
|
||||||
|
|
||||||
txhash = self.policy_agent.contract.functions.createPolicy(self.id, self.miner._ether_address,
|
txhash = self.policy_agent.contract.functions.createPolicy(self.id,
|
||||||
|
self.miner.checksum_public_address,
|
||||||
self.lock_periods).transact(payload)
|
self.lock_periods).transact(payload)
|
||||||
self.policy_agent.blockchain.wait_for_receipt(txhash)
|
self.policy_agent.blockchain.wait_for_receipt(txhash)
|
||||||
|
|
||||||
|
@ -91,7 +94,11 @@ class BlockchainPolicy(Policy):
|
||||||
class NotEnoughBlockchainUrsulas(Exception):
|
class NotEnoughBlockchainUrsulas(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, author: PolicyAuthor, *args, **kwargs) -> None:
|
def __init__(self,
|
||||||
|
author: PolicyAuthor,
|
||||||
|
*args, **kwargs
|
||||||
|
) -> None:
|
||||||
|
|
||||||
self.author = author
|
self.author = author
|
||||||
super().__init__(alice=author, *args, **kwargs)
|
super().__init__(alice=author, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -104,14 +111,18 @@ class BlockchainPolicy(Policy):
|
||||||
duration = end_block - start_block
|
duration = end_block - start_block
|
||||||
|
|
||||||
miner = Miner(address=miner_address, miner_agent=self.author.policy_agent.miner_agent)
|
miner = Miner(address=miner_address, miner_agent=self.author.policy_agent.miner_agent)
|
||||||
arrangement = BlockchainArrangement(author=self.author, miner=miner, lock_periods=duration)
|
arrangement = BlockchainArrangement(author=self.author,
|
||||||
|
miner=miner,
|
||||||
|
value=rate*duration, # TODO Check the math/types here
|
||||||
|
lock_periods=duration,
|
||||||
|
expiration=end_block) # TODO: fix missing argument here
|
||||||
|
|
||||||
arrangement.is_published = True
|
arrangement.is_published = True
|
||||||
return arrangement
|
return arrangement
|
||||||
|
|
||||||
def __find_ursulas(self, ether_addresses: List[str], target_quantity: int, timeout: int = 120):
|
def __find_ursulas(self, ether_addresses: List[str], target_quantity: int, timeout: int = 120):
|
||||||
start_time = maya.now() # Marker for timeout calculation
|
start_time = maya.now() # Marker for timeout calculation
|
||||||
found_ursulas, unknown_addresses = set(), deque()
|
found_ursulas, unknown_addresses = set(), deque() # type: set, deque
|
||||||
while len(found_ursulas) < target_quantity:
|
while len(found_ursulas) < target_quantity:
|
||||||
|
|
||||||
# Check for a timeout
|
# Check for a timeout
|
||||||
|
@ -146,24 +157,27 @@ class BlockchainPolicy(Policy):
|
||||||
|
|
||||||
return found_ursulas
|
return found_ursulas
|
||||||
|
|
||||||
def make_arrangements(self, network_middleware: RestMiddleware,
|
def make_arrangements(self,
|
||||||
deposit: int, expiration: maya.MayaDT,
|
network_middleware: RestMiddleware,
|
||||||
handpicked_ursulas: Set[Ursula] = set()) -> None:
|
deposit: int,
|
||||||
|
expiration: maya.MayaDT,
|
||||||
|
handpicked_ursulas: Set[Ursula] = None
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create and consider n Arrangements from sampled miners, a list of Ursulas, or a combination of both.
|
Create and consider n Arrangements from sampled miners, a list of Ursulas, or a combination of both.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ADDITIONAL_URSULAS = 1.5 # TODO: Make constant
|
ADDITIONAL_URSULAS = 1.5 # TODO: Make constant
|
||||||
|
|
||||||
|
handpicked_ursulas = handpicked_ursulas or set() # type: set
|
||||||
target_sample_quantity = self.n - len(handpicked_ursulas)
|
target_sample_quantity = self.n - len(handpicked_ursulas)
|
||||||
|
|
||||||
selected_addresses = set()
|
selected_addresses = set() # type: set
|
||||||
try: # Sample by reading from the Blockchain
|
try: # Sample by reading from the Blockchain
|
||||||
actual_sample_quantity = math.ceil(target_sample_quantity * ADDITIONAL_URSULAS)
|
actual_sample_quantity = math.ceil(target_sample_quantity * ADDITIONAL_URSULAS)
|
||||||
duration = int(calculate_period_duration(expiration))
|
duration = int(calculate_period_duration(expiration))
|
||||||
sampled_addresses = self.alice.recruit(quantity=actual_sample_quantity,
|
sampled_addresses = self.alice.recruit(quantity=actual_sample_quantity,
|
||||||
duration=duration,
|
duration=duration)
|
||||||
)
|
|
||||||
except MinerAgent.NotEnoughMiners:
|
except MinerAgent.NotEnoughMiners:
|
||||||
error = "Cannot create policy with {} arrangements."
|
error = "Cannot create policy with {} arrangements."
|
||||||
raise self.NotEnoughBlockchainUrsulas(error.format(self.n))
|
raise self.NotEnoughBlockchainUrsulas(error.format(self.n))
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from constant_sorrow import constants
|
from constant_sorrow import constants
|
||||||
|
|
||||||
# from nucypher.config.config import DEFAULT_CONFIG_ROOT, NodeConfiguration
|
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||||
# from nucypher.config.parsers import parse_blockchain_config
|
|
||||||
|
|
||||||
|
|
||||||
class EthereumContractRegistry:
|
class EthereumContractRegistry:
|
||||||
|
@ -17,7 +18,8 @@ class EthereumContractRegistry:
|
||||||
WARNING: Unless you are developing NuCypher, you most likely won't ever need
|
WARNING: Unless you are developing NuCypher, you most likely won't ever need
|
||||||
to use this.
|
to use this.
|
||||||
"""
|
"""
|
||||||
# __default_registry_path = os.path.join(DEFAULT_CONFIG_ROOT, 'registry.json')
|
# TODO: Integrate with config classes
|
||||||
|
__default_registry_path = os.path.join(DEFAULT_CONFIG_ROOT, 'contract_registry.json')
|
||||||
|
|
||||||
class RegistryError(Exception):
|
class RegistryError(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -32,18 +34,17 @@ class EthereumContractRegistry:
|
||||||
self.__registry_filepath = registry_filepath or self.__default_registry_path
|
self.__registry_filepath = registry_filepath or self.__default_registry_path
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_config(cls, config) -> 'EthereumContractRegistry':
|
def from_config(cls, config) -> Union['EthereumContractRegistry', 'TemporaryEthereumContractRegistry']:
|
||||||
if config.temp_registry is True: # In memory only
|
if config.temp_registry is True: # In memory only
|
||||||
registry = TemporaryEthereumContractRegistry()
|
return TemporaryEthereumContractRegistry()
|
||||||
else:
|
else:
|
||||||
registry = EthereumContractRegistry()
|
return EthereumContractRegistry()
|
||||||
return registry
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def registry_filepath(self):
|
def registry_filepath(self):
|
||||||
return self.__registry_filepath
|
return self.__registry_filepath
|
||||||
|
|
||||||
def _swap_registry(self, filepath: str) -> True:
|
def _swap_registry(self, filepath: str) -> bool:
|
||||||
self.__registry_filepath = filepath
|
self.__registry_filepath = filepath
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -8,19 +8,27 @@ from solc.exceptions import SolcError
|
||||||
|
|
||||||
|
|
||||||
class SolidityCompiler:
|
class SolidityCompiler:
|
||||||
|
|
||||||
|
# TODO: Integrate with config classes
|
||||||
|
|
||||||
__default_version = 'v0.4.24'
|
__default_version = 'v0.4.24'
|
||||||
__default_configuration_path = os.path.join(dirname(abspath(__file__)), './compiler.json')
|
__default_configuration_path = os.path.join(dirname(abspath(__file__)), './compiler.json')
|
||||||
|
|
||||||
__default_sol_binary_path = shutil.which('solc')
|
__default_sol_binary_path = shutil.which('solc')
|
||||||
if __default_sol_binary_path is None:
|
if __default_sol_binary_path is None:
|
||||||
__bin_path = os.path.dirname(shutil.which('python'))
|
__bin_path = os.path.dirname(shutil.which('python')) # type: str
|
||||||
__default_sol_binary_path = os.path.join(__bin_path, 'solc')
|
__default_sol_binary_path = os.path.join(__bin_path, 'solc') # type: str
|
||||||
|
|
||||||
__default_contract_dir = os.path.join(dirname(abspath(__file__)), 'source', 'contracts')
|
__default_contract_dir = os.path.join(dirname(abspath(__file__)), 'source', 'contracts')
|
||||||
__default_chain_name = 'tester'
|
__default_chain_name = 'tester'
|
||||||
|
|
||||||
def __init__(self, solc_binary_path=None, configuration_path=None,
|
def __init__(self,
|
||||||
chain_name=None, contract_dir=None, test_contract_dir=None) -> None:
|
solc_binary_path: str = None,
|
||||||
|
configuration_path: str = None,
|
||||||
|
chain_name: str = None,
|
||||||
|
contract_dir: str = None,
|
||||||
|
test_contract_dir: str= None
|
||||||
|
) -> None:
|
||||||
|
|
||||||
# Compiler binary and root solidity source code directory
|
# Compiler binary and root solidity source code directory
|
||||||
self.__sol_binary_path = solc_binary_path if solc_binary_path is not None else self.__default_sol_binary_path
|
self.__sol_binary_path = solc_binary_path if solc_binary_path is not None else self.__default_sol_binary_path
|
||||||
|
@ -34,7 +42,7 @@ class SolidityCompiler:
|
||||||
# Set the local env's solidity compiler binary
|
# Set the local env's solidity compiler binary
|
||||||
os.environ['SOLC_BINARY'] = self.__sol_binary_path
|
os.environ['SOLC_BINARY'] = self.__sol_binary_path
|
||||||
|
|
||||||
def install_compiler(self, version=None):
|
def install_compiler(self, version: str=None):
|
||||||
"""
|
"""
|
||||||
Installs the specified solidity compiler version.
|
Installs the specified solidity compiler version.
|
||||||
https://github.com/ethereum/py-solc#installing-the-solc-binary
|
https://github.com/ethereum/py-solc#installing-the-solc-binary
|
||||||
|
|
|
@ -13,8 +13,9 @@ from nacl.exceptions import CryptoError
|
||||||
from nacl.secret import SecretBox
|
from nacl.secret import SecretBox
|
||||||
from umbral.keys import UmbralPrivateKey
|
from umbral.keys import UmbralPrivateKey
|
||||||
|
|
||||||
from nucypher.config.constants import DEFAULT_KEYRING_ROOT
|
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||||
from nucypher.config.utils import validate_passphrase, NucypherConfigurationError
|
from nucypher.config.node import NodeConfiguration
|
||||||
|
from nucypher.config.utils import validate_passphrase
|
||||||
from nucypher.crypto.powers import SigningPower, EncryptingPower, CryptoPower
|
from nucypher.crypto.powers import SigningPower, EncryptingPower, CryptoPower
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ def _parse_keyfile(keypath: str):
|
||||||
try:
|
try:
|
||||||
key_metadata = json.loads(keyfile)
|
key_metadata = json.loads(keyfile)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
raise NucypherConfigurationError("Invalid data in keyfile {}".format(keypath))
|
raise NodeConfiguration.ConfigurationError("Invalid data in keyfile {}".format(keypath))
|
||||||
else:
|
else:
|
||||||
return key_metadata
|
return key_metadata
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ def _save_private_keyfile(keypath: str, key_data: dict) -> str:
|
||||||
mode = stat.S_IRUSR | stat.S_IWUSR # 0o600
|
mode = stat.S_IRUSR | stat.S_IWUSR # 0o600
|
||||||
|
|
||||||
try:
|
try:
|
||||||
keyfile_descriptor = os.open(path=keypath, flags=flags, mode=mode)
|
keyfile_descriptor = os.open(file=keypath, flags=flags, mode=mode)
|
||||||
finally:
|
finally:
|
||||||
os.umask(0) # Set the umask to 0 after opening
|
os.umask(0) # Set the umask to 0 after opening
|
||||||
|
|
||||||
|
@ -87,10 +88,10 @@ def _save_public_keyfile(keypath: str, key_data: bytes) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL # Write, Create, Non-Existing
|
flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL # Write, Create, Non-Existing
|
||||||
mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH # 0o644
|
mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH # 0o644
|
||||||
|
|
||||||
try:
|
try:
|
||||||
keyfile_descriptor = os.open(path=keypath, flags=flags, mode=mode)
|
keyfile_descriptor = os.open(file=keypath, flags=flags, mode=mode)
|
||||||
finally:
|
finally:
|
||||||
os.umask(0) # Set the umask to 0 after opening
|
os.umask(0) # Set the umask to 0 after opening
|
||||||
|
|
||||||
|
@ -154,7 +155,10 @@ def _encrypt_umbral_key(wrapping_key: bytes, umbral_key: UmbralPrivateKey) -> di
|
||||||
return crypto_data
|
return crypto_data
|
||||||
|
|
||||||
|
|
||||||
def _decrypt_umbral_key(wrapping_key: bytes, nonce: bytes, enc_key_material: bytes) -> UmbralPrivateKey:
|
def _decrypt_umbral_key(wrapping_key: bytes,
|
||||||
|
nonce: bytes,
|
||||||
|
enc_key_material: bytes
|
||||||
|
) -> UmbralPrivateKey:
|
||||||
"""
|
"""
|
||||||
Decrypts an encrypted key with nacl's XSalsa20-Poly1305 algorithm (SecretBox).
|
Decrypts an encrypted key with nacl's XSalsa20-Poly1305 algorithm (SecretBox).
|
||||||
Returns a decrypted key as an UmbralPrivateKey.
|
Returns a decrypted key as an UmbralPrivateKey.
|
||||||
|
@ -209,7 +213,8 @@ class NucypherKeyring:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__default_keyring_root = DEFAULT_KEYRING_ROOT
|
# TODO: Make lazy for better integration with config classes
|
||||||
|
__default_keyring_root = os.path.join(DEFAULT_CONFIG_ROOT, "keyring")
|
||||||
|
|
||||||
__default_public_key_dir = os.path.join(__default_keyring_root, 'public')
|
__default_public_key_dir = os.path.join(__default_keyring_root, 'public')
|
||||||
__default_private_key_dir = os.path.join(__default_keyring_root, 'private')
|
__default_private_key_dir = os.path.join(__default_keyring_root, 'private')
|
||||||
|
@ -228,7 +233,8 @@ class NucypherKeyring:
|
||||||
class KeyringLocked(KeyringError):
|
class KeyringLocked(KeyringError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, root_key_path: str=None,
|
def __init__(self,
|
||||||
|
root_key_path: str=None,
|
||||||
pub_root_key_path: str=None,
|
pub_root_key_path: str=None,
|
||||||
signing_key_path: str=None,
|
signing_key_path: str=None,
|
||||||
pub_signing_key_path: str=None,
|
pub_signing_key_path: str=None,
|
||||||
|
@ -280,6 +286,7 @@ class NucypherKeyring:
|
||||||
if self.__derived_key_material is not None:
|
if self.__derived_key_material is not None:
|
||||||
raise Exception('Keyring already unlocked')
|
raise Exception('Keyring already unlocked')
|
||||||
|
|
||||||
|
# TODO: missing salt parameter below
|
||||||
derived_key = _derive_key_material_from_passphrase(passphrase=passphrase)
|
derived_key = _derive_key_material_from_passphrase(passphrase=passphrase)
|
||||||
self.__derived_key_material = derived_key
|
self.__derived_key_material = derived_key
|
||||||
|
|
||||||
|
@ -313,7 +320,12 @@ class NucypherKeyring:
|
||||||
return new_cryptopower
|
return new_cryptopower
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate(cls, passphrase: str, encryption: bool=True, transacting: bool=True, output_path: str=None) -> 'NucypherKeyring':
|
def generate(cls,
|
||||||
|
passphrase: str,
|
||||||
|
encryption: bool = True,
|
||||||
|
transacting: bool = True,
|
||||||
|
output_path: str = None
|
||||||
|
) -> 'NucypherKeyring':
|
||||||
"""
|
"""
|
||||||
Generates new encryption, signing, and transacting keys encrypted with the passphrase,
|
Generates new encryption, signing, and transacting keys encrypted with the passphrase,
|
||||||
respectively saving keyfiles on the local filesystem from *default* paths,
|
respectively saving keyfiles on the local filesystem from *default* paths,
|
||||||
|
@ -337,10 +349,10 @@ class NucypherKeyring:
|
||||||
os.mkdir(_private_key_dir, mode=0o700) # private
|
os.mkdir(_private_key_dir, mode=0o700) # private
|
||||||
|
|
||||||
# Generate keys
|
# Generate keys
|
||||||
keyring_args = dict()
|
keyring_args = dict() # type: dict
|
||||||
if encryption is True:
|
if encryption is True:
|
||||||
enc_privkey, enc_pubkey = _generate_encryption_keys()
|
enc_privkey, enc_pubkey = _generate_encryption_keys()
|
||||||
sig_privkey, enc_pubkey = _generate_signing_keys()
|
sig_privkey, sig_pubkey = _generate_signing_keys()
|
||||||
|
|
||||||
passphrase_salt = os.urandom(32)
|
passphrase_salt = os.urandom(32)
|
||||||
enc_salt = os.urandom(32)
|
enc_salt = os.urandom(32)
|
||||||
|
@ -350,21 +362,21 @@ class NucypherKeyring:
|
||||||
enc_wrap_key = _derive_wrapping_key_from_key_material(enc_salt, der_key_material)
|
enc_wrap_key = _derive_wrapping_key_from_key_material(enc_salt, der_key_material)
|
||||||
sig_wrap_key = _derive_wrapping_key_from_key_material(sig_salt, der_key_material)
|
sig_wrap_key = _derive_wrapping_key_from_key_material(sig_salt, der_key_material)
|
||||||
|
|
||||||
enc_json = _encrypt_umbral_key(der_wrap_key, enc_key)
|
enc_json = _encrypt_umbral_key(der_key_material, enc_wrap_key)
|
||||||
sig_json = _encrypt_umbral_key(der_wrap_key, sig_key)
|
sig_json = _encrypt_umbral_key(der_key_material, sig_wrap_key)
|
||||||
|
|
||||||
enc_json['master_salt'] = urlsafe_b64encode(salt).decode()
|
enc_json['master_salt'] = urlsafe_b64encode(enc_salt).decode()
|
||||||
sig_json['master_salt'] = urlsafe_b64encode(salt).decode()
|
sig_json['master_salt'] = urlsafe_b64encode(sig_salt).decode()
|
||||||
|
|
||||||
enc_json['wrap_salt'] = urlsafe_b64encode(salt).decode()
|
enc_json['wrap_salt'] = urlsafe_b64encode(enc_salt).decode()
|
||||||
sig_json['wrap_salt'] = urlsafe_b64encode(salt).decode()
|
sig_json['wrap_salt'] = urlsafe_b64encode(sig_salt).decode()
|
||||||
|
|
||||||
# Write private keys to files
|
# Write private keys to files
|
||||||
rootkey_path = _save_private_keyfile(cls.__default_key_filepaths['root'], enc_json)
|
rootkey_path = _save_private_keyfile(cls.__default_key_filepaths['root'], enc_json)
|
||||||
sigkey_path = _save_private_keyfile(cls.__default_key_filepaths['signing'], sig_json)
|
sigkey_path = _save_private_keyfile(cls.__default_key_filepaths['signing'], sig_json)
|
||||||
|
|
||||||
bytes_enc_pubkey = enc_pubkey.to_bytes(encoder=urlsafe_b64encoder)
|
bytes_enc_pubkey = enc_pubkey.to_bytes(encoder=urlsafe_b64encode)
|
||||||
bytes_sig_pubkey = sig_pubkey.to_bytes(encoder=urlsafe_b64encoder)
|
bytes_sig_pubkey = sig_pubkey.to_bytes(encoder=urlsafe_b64encode)
|
||||||
|
|
||||||
# Write public keys to files
|
# Write public keys to files
|
||||||
rootkey_pub_path = _save_public_keyfile(
|
rootkey_pub_path = _save_public_keyfile(
|
||||||
|
|
|
@ -100,7 +100,7 @@ def parse_alice_config(config=None, filepath=DEFAULT_CONFIG_FILE_LOCATION) -> di
|
||||||
|
|
||||||
character_payload = parse_character_config(config=config)
|
character_payload = parse_character_config(config=config)
|
||||||
|
|
||||||
alice_payload = dict() # Alice specific
|
alice_payload = dict() # type: dict # Alice specific
|
||||||
|
|
||||||
character_payload.update(alice_payload)
|
character_payload.update(alice_payload)
|
||||||
|
|
||||||
|
|
|
@ -28,10 +28,10 @@ class NoBlockchainPower(PowerUpError):
|
||||||
|
|
||||||
|
|
||||||
class CryptoPower(object):
|
class CryptoPower(object):
|
||||||
def __init__(self, power_ups=None) -> None:
|
def __init__(self, power_ups: dict = None) -> None:
|
||||||
self._power_ups = {}
|
self._power_ups = {} # type: dict
|
||||||
# TODO: The keys here will actually be IDs for looking up in a KeyStore.
|
# TODO: The keys here will actually be IDs for looking up in a KeyStore.
|
||||||
self.public_keys = {}
|
self.public_keys = {} # type: dict
|
||||||
|
|
||||||
if power_ups is not None:
|
if power_ups is not None:
|
||||||
for power_up in power_ups:
|
for power_up in power_ups:
|
||||||
|
|
|
@ -15,11 +15,16 @@ class SuspiciousActivity(RuntimeError):
|
||||||
|
|
||||||
|
|
||||||
class NucypherHashProtocol(KademliaProtocol):
|
class NucypherHashProtocol(KademliaProtocol):
|
||||||
def __init__(self, sourceNode, storage, ksize, *args, **kwargs) -> None:
|
def __init__(self,
|
||||||
|
sourceNode,
|
||||||
|
storage,
|
||||||
|
ksize,
|
||||||
|
*args, **kwargs) -> None:
|
||||||
|
|
||||||
super().__init__(sourceNode, storage, ksize, *args, **kwargs)
|
super().__init__(sourceNode, storage, ksize, *args, **kwargs)
|
||||||
|
|
||||||
self.router = NucypherRoutingTable(self, ksize, sourceNode)
|
self.router = NucypherRoutingTable(self, ksize, sourceNode)
|
||||||
self.illegal_keys_seen = [] # TODO: 340
|
self.illegal_keys_seen = [] # type: list # TODO: 340
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ursulas(self):
|
def ursulas(self):
|
||||||
|
|
|
@ -2,7 +2,7 @@ import binascii
|
||||||
import uuid
|
import uuid
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import Generator, List, Set
|
from typing import Generator, List, Set, Iterable
|
||||||
|
|
||||||
import maya
|
import maya
|
||||||
import msgpack
|
import msgpack
|
||||||
|
@ -10,6 +10,7 @@ from bytestring_splitter import BytestringSplitter, VariableLengthBytestring
|
||||||
from constant_sorrow import constants
|
from constant_sorrow import constants
|
||||||
from eth_utils import to_canonical_address, to_checksum_address
|
from eth_utils import to_canonical_address, to_checksum_address
|
||||||
from umbral.config import default_params
|
from umbral.config import default_params
|
||||||
|
from umbral.fragments import KFrag
|
||||||
from umbral.pre import Capsule
|
from umbral.pre import Capsule
|
||||||
|
|
||||||
from nucypher.characters.lawful import Alice
|
from nucypher.characters.lawful import Alice
|
||||||
|
@ -20,6 +21,7 @@ from nucypher.crypto.kits import UmbralMessageKit
|
||||||
from nucypher.crypto.powers import SigningPower, EncryptingPower
|
from nucypher.crypto.powers import SigningPower, EncryptingPower
|
||||||
from nucypher.crypto.signing import Signature
|
from nucypher.crypto.signing import Signature
|
||||||
from nucypher.crypto.splitters import key_splitter
|
from nucypher.crypto.splitters import key_splitter
|
||||||
|
from nucypher.network.middleware import RestMiddleware
|
||||||
|
|
||||||
|
|
||||||
class Arrangement:
|
class Arrangement:
|
||||||
|
@ -104,26 +106,32 @@ class Policy:
|
||||||
and generates a TreasureMap for the Policy, recording which Ursulas got a KFrag.
|
and generates a TreasureMap for the Policy, recording which Ursulas got a KFrag.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, alice, label, bob=None, kfrags=(constants.UNKNOWN_KFRAG,),
|
def __init__(self,
|
||||||
public_key=None, m=None, alices_signature=constants.NOT_SIGNED) -> None:
|
alice,
|
||||||
|
label,
|
||||||
|
bob=None,
|
||||||
|
kfrags=(constants.UNKNOWN_KFRAG,),
|
||||||
|
public_key=None,
|
||||||
|
m: int = None,
|
||||||
|
alices_signature=constants.NOT_SIGNED) -> None:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
:param kfrags: A list of KFrags to distribute per this Policy.
|
:param kfrags: A list of KFrags to distribute per this Policy.
|
||||||
:param label: The identity of the resource to which Bob is granted access.
|
:param label: The identity of the resource to which Bob is granted access.
|
||||||
"""
|
"""
|
||||||
self.alice = alice
|
self.alice = alice # type: Alice
|
||||||
self.label = label
|
self.label = label # type: bytes
|
||||||
self.bob = bob
|
self.bob = bob # type: Bob
|
||||||
self.kfrags = kfrags
|
self.kfrags = kfrags # type: List[KFrag]
|
||||||
self.public_key = public_key
|
self.public_key = public_key
|
||||||
self.treasure_map = TreasureMap(m=m)
|
self.treasure_map = TreasureMap(m=m)
|
||||||
|
|
||||||
# Keep track of this stuff
|
# Keep track of this stuff
|
||||||
self._accepted_arrangements = set()
|
self._accepted_arrangements = set() # type: set
|
||||||
self._rejected_arrangements = set()
|
self._rejected_arrangements = set() # type: set
|
||||||
|
|
||||||
self._enacted_arrangements = OrderedDict()
|
self._enacted_arrangements = OrderedDict() # type: OrderedDict
|
||||||
self._published_arrangements = OrderedDict()
|
self._published_arrangements = OrderedDict() # type: OrderedDict
|
||||||
|
|
||||||
self.alices_signature = alices_signature
|
self.alices_signature = alices_signature
|
||||||
|
|
||||||
|
@ -134,10 +142,10 @@ class Policy:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def n(self):
|
def n(self) -> int:
|
||||||
return len(self.kfrags)
|
return len(self.kfrags)
|
||||||
|
|
||||||
def hrac(self):
|
def hrac(self) -> bytes:
|
||||||
"""
|
"""
|
||||||
This function is hanging on for dear life. After 180 is closed, it can be completely deprecated.
|
This function is hanging on for dear life. After 180 is closed, it can be completely deprecated.
|
||||||
|
|
||||||
|
@ -153,7 +161,7 @@ class Policy:
|
||||||
"""
|
"""
|
||||||
return keccak_digest(bytes(self.alice.stamp) + bytes(self.bob.stamp) + self.label)
|
return keccak_digest(bytes(self.alice.stamp) + bytes(self.bob.stamp) + self.label)
|
||||||
|
|
||||||
def publish_treasure_map(self, network_middleare):
|
def publish_treasure_map(self, network_middleware: RestMiddleware) -> dict:
|
||||||
self.treasure_map.prepare_for_publication(self.bob.public_keys(EncryptingPower),
|
self.treasure_map.prepare_for_publication(self.bob.public_keys(EncryptingPower),
|
||||||
self.bob.public_keys(SigningPower),
|
self.bob.public_keys(SigningPower),
|
||||||
self.alice.stamp,
|
self.alice.stamp,
|
||||||
|
@ -163,14 +171,13 @@ class Policy:
|
||||||
# TODO: Optionally block.
|
# TODO: Optionally block.
|
||||||
raise RuntimeError("Alice hasn't learned of any nodes. Thus, she can't push the TreasureMap.")
|
raise RuntimeError("Alice hasn't learned of any nodes. Thus, she can't push the TreasureMap.")
|
||||||
|
|
||||||
responses = {}
|
responses = dict()
|
||||||
|
|
||||||
for node in self.alice.known_nodes.values():
|
for node in self.alice.known_nodes.values():
|
||||||
# TODO: It's way overkill to push this to every node we know about. Come up with a system. 342
|
# TODO: It's way overkill to push this to every node we know about. Come up with a system. 342
|
||||||
response = network_middleare.put_treasure_map_on_node(node,
|
response = network_middleware.put_treasure_map_on_node(node,
|
||||||
self.treasure_map.public_id(),
|
self.treasure_map.public_id(),
|
||||||
bytes(self.treasure_map)
|
bytes(self.treasure_map)
|
||||||
)
|
) # TODO: Certificate filepath needs to be looked up and passed here
|
||||||
if response.status_code == 202:
|
if response.status_code == 202:
|
||||||
responses[node] = response
|
responses[node] = response
|
||||||
# TODO: Handle response wherein node already had a copy of this TreasureMap. 341
|
# TODO: Handle response wherein node already had a copy of this TreasureMap. 341
|
||||||
|
@ -180,9 +187,9 @@ class Policy:
|
||||||
|
|
||||||
return responses
|
return responses
|
||||||
|
|
||||||
def publish(self, network_middleware) -> None:
|
def publish(self, network_middleware: RestMiddleware) -> dict:
|
||||||
"""Spread word of this Policy far and wide."""
|
"""Spread word of this Policy far and wide."""
|
||||||
return self.publish_treasure_map(network_middleare=network_middleware)
|
return self.publish_treasure_map(network_middleware=network_middleware)
|
||||||
|
|
||||||
def __assign_kfrags(self) -> Generator[Arrangement, None, None]:
|
def __assign_kfrags(self) -> Generator[Arrangement, None, None]:
|
||||||
|
|
||||||
|
@ -203,8 +210,9 @@ class Policy:
|
||||||
# This is ideally an impossible situation, because we don't typically
|
# This is ideally an impossible situation, because we don't typically
|
||||||
# enter this method unless we've already had n or more Arrangements accepted.
|
# enter this method unless we've already had n or more Arrangements accepted.
|
||||||
raise self.MoreKFragsThanArrangements("Not enough accepted arrangements to assign all KFrags.")
|
raise self.MoreKFragsThanArrangements("Not enough accepted arrangements to assign all KFrags.")
|
||||||
|
return
|
||||||
|
|
||||||
def enact(self, network_middleware, publish=True) -> None:
|
def enact(self, network_middleware, publish=True) -> dict:
|
||||||
"""
|
"""
|
||||||
Assign kfrags to ursulas_on_network, and distribute them via REST,
|
Assign kfrags to ursulas_on_network, and distribute them via REST,
|
||||||
populating enacted_arrangements
|
populating enacted_arrangements
|
||||||
|
@ -247,17 +255,21 @@ class Policy:
|
||||||
return negotiation_result
|
return negotiation_result
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def make_arrangements(self, network_middleware,
|
def make_arrangements(self,
|
||||||
|
network_middleware: RestMiddleware,
|
||||||
deposit: int,
|
deposit: int,
|
||||||
expiration: maya.MayaDT,
|
expiration: maya.MayaDT,
|
||||||
ursulas: List[Ursula] = None) -> None:
|
ursulas: Set[Ursula] = None) -> None:
|
||||||
"""
|
"""
|
||||||
Create and consider n Arangement objects.
|
Create and consider n Arangement objects.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def _consider_arrangements(self, network_middleware, candidate_ursulas: Set[Ursula],
|
def _consider_arrangements(self,
|
||||||
deposit: int, expiration: maya.MayaDT) -> tuple:
|
network_middleware: RestMiddleware,
|
||||||
|
candidate_ursulas: Set[Ursula],
|
||||||
|
deposit: int,
|
||||||
|
expiration: maya.MayaDT) -> tuple:
|
||||||
|
|
||||||
for selected_ursula in candidate_ursulas:
|
for selected_ursula in candidate_ursulas:
|
||||||
arrangement = self._arrangement_class(alice=self.alice,
|
arrangement = self._arrangement_class(alice=self.alice,
|
||||||
|
@ -278,22 +290,24 @@ class FederatedPolicy(Policy):
|
||||||
self.ursulas = ursulas
|
self.ursulas = ursulas
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def make_arrangements(self, network_middleware,
|
def make_arrangements(self,
|
||||||
|
network_middleware: RestMiddleware,
|
||||||
deposit: int,
|
deposit: int,
|
||||||
expiration: maya.MayaDT,
|
expiration: maya.MayaDT,
|
||||||
handpicked_ursulas: Set[Ursula] = None) -> None:
|
handpicked_ursulas: Set[Ursula] = None) -> None:
|
||||||
|
|
||||||
if handpicked_ursulas is None:
|
if handpicked_ursulas is None:
|
||||||
ursulas = set()
|
ursulas = set() # type: set
|
||||||
else:
|
else:
|
||||||
ursulas = handpicked_ursulas
|
ursulas = handpicked_ursulas
|
||||||
ursulas.update(self.ursulas)
|
ursulas.update(self.ursulas)
|
||||||
|
|
||||||
if len(ursulas) < self.n:
|
if len(ursulas) < self.n:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"To make a Policy in federated mode, you need to designate *all*\
|
"To make a Policy in federated mode, you need to designate *all* ' \
|
||||||
the Ursulas you need (in this case, {}); there's no other way to\
|
the Ursulas you need (in this case, {}); there's no other way to ' \
|
||||||
know which nodes to use. Either pass them here or when you make\
|
know which nodes to use. Either pass them here or when you make ' \
|
||||||
the Policy.".format(self.n))
|
the Policy.".format(self.n))
|
||||||
|
|
||||||
# TODO: One of these layers needs to add concurrency.
|
# TODO: One of these layers needs to add concurrency.
|
||||||
|
|
||||||
|
@ -317,17 +331,17 @@ class TreasureMap:
|
||||||
"""Raised when the public signature (typically intended for Ursula) is not valid."""
|
"""Raised when the public signature (typically intended for Ursula) is not valid."""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
m=None,
|
m: int = None,
|
||||||
destinations=None,
|
destinations=None,
|
||||||
message_kit=None,
|
message_kit: UmbralMessageKit= None,
|
||||||
public_signature=None,
|
public_signature: Signature = None,
|
||||||
hrac=None) -> None:
|
hrac=None) -> None:
|
||||||
|
|
||||||
if m is not None:
|
if m is not None:
|
||||||
if m > 255:
|
if m > 255:
|
||||||
raise ValueError(
|
raise ValueError("Largest allowed value for m is 255.")
|
||||||
"Largest allowed value for m is 255. Why the heck are you trying to make it larger than that anyway? That's too big.")
|
|
||||||
self.m = m
|
self.m = m
|
||||||
|
|
||||||
self.destinations = destinations or {}
|
self.destinations = destinations or {}
|
||||||
else:
|
else:
|
||||||
self.m = constants.NO_DECRYPTION_PERFORMED
|
self.m = constants.NO_DECRYPTION_PERFORMED
|
||||||
|
@ -339,7 +353,12 @@ class TreasureMap:
|
||||||
self._hrac = hrac
|
self._hrac = hrac
|
||||||
self._payload = None
|
self._payload = None
|
||||||
|
|
||||||
def prepare_for_publication(self, bob_encrypting_key, bob_verifying_key, alice_stamp, label):
|
def prepare_for_publication(self,
|
||||||
|
bob_encrypting_key,
|
||||||
|
bob_verifying_key,
|
||||||
|
alice_stamp,
|
||||||
|
label):
|
||||||
|
|
||||||
plaintext = self.m.to_bytes(1, "big") + self.nodes_as_bytes()
|
plaintext = self.m.to_bytes(1, "big") + self.nodes_as_bytes()
|
||||||
|
|
||||||
self.message_kit, _signature_for_bob = encrypt_and_sign(bob_encrypting_key,
|
self.message_kit, _signature_for_bob = encrypt_and_sign(bob_encrypting_key,
|
||||||
|
@ -502,7 +521,7 @@ class WorkOrder(object):
|
||||||
class WorkOrderHistory:
|
class WorkOrderHistory:
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.by_ursula = {}
|
self.by_ursula = {} # type: dict
|
||||||
|
|
||||||
def __contains__(self, item):
|
def __contains__(self, item):
|
||||||
assert False
|
assert False
|
||||||
|
@ -521,7 +540,7 @@ class WorkOrderHistory:
|
||||||
return self.by_ursula.keys()
|
return self.by_ursula.keys()
|
||||||
|
|
||||||
def by_capsule(self, capsule):
|
def by_capsule(self, capsule):
|
||||||
ursulas_by_capsules = {}
|
ursulas_by_capsules = {} # type: dict
|
||||||
for ursula, capsules in self.by_ursula.items():
|
for ursula, capsules in self.by_ursula.items():
|
||||||
for saved_capsule, work_order in capsules.items():
|
for saved_capsule, work_order in capsules.items():
|
||||||
if saved_capsule == capsule:
|
if saved_capsule == capsule:
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import List
|
from typing import List, Set
|
||||||
|
|
||||||
import maya
|
import maya
|
||||||
|
|
||||||
from nucypher.characters.lawful import Ursula
|
from nucypher.characters.lawful import Ursula
|
||||||
|
from nucypher.network.middleware import RestMiddleware
|
||||||
from nucypher.policy.models import Arrangement, Policy
|
from nucypher.policy.models import Arrangement, Policy
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,20 +19,24 @@ class MockArrangement(Arrangement):
|
||||||
|
|
||||||
|
|
||||||
class MockPolicy(Policy):
|
class MockPolicy(Policy):
|
||||||
def make_arrangements(self, network_middleware,
|
def make_arrangements(self,
|
||||||
|
network_middleware: RestMiddleware,
|
||||||
deposit: int,
|
deposit: int,
|
||||||
expiration: maya.MayaDT,
|
expiration: maya.MayaDT,
|
||||||
ursulas: List[Ursula] = None) -> None:
|
ursulas: Set[Ursula] = None
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create and consider n Arangement objects from all known nodes.
|
Create and consider n Arangement objects from all known nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for ursula in self.alice._known_nodes:
|
for ursula in self.alice.known_nodes:
|
||||||
arrangement = MockArrangement(alice=self.alice, ursula=ursula,
|
arrangement = MockArrangement(alice=self.alice, ursula=ursula,
|
||||||
hrac=self.hrac(),
|
hrac=self.hrac(),
|
||||||
expiration=expiration)
|
expiration=expiration)
|
||||||
|
|
||||||
self.consider_arrangement(network_middleware=network_middleware, arrangement=arrangement)
|
self.consider_arrangement(network_middleware=network_middleware,
|
||||||
|
ursula=ursula,
|
||||||
|
arrangement=arrangement)
|
||||||
|
|
||||||
|
|
||||||
class MockPolicyCreation:
|
class MockPolicyCreation:
|
||||||
|
|
|
@ -50,7 +50,7 @@ def test_alice_sets_treasure_map(enacted_federated_policy, federated_ursulas):
|
||||||
Having enacted all the policies of a PolicyGroup, Alice creates a TreasureMap and ...... TODO
|
Having enacted all the policies of a PolicyGroup, Alice creates a TreasureMap and ...... TODO
|
||||||
"""
|
"""
|
||||||
|
|
||||||
enacted_federated_policy.publish_treasure_map(network_middleare=MockRestMiddleware())
|
enacted_federated_policy.publish_treasure_map(network_middleware=MockRestMiddleware())
|
||||||
|
|
||||||
treasure_map_as_set_on_network = list(federated_ursulas)[0].treasure_maps[
|
treasure_map_as_set_on_network = list(federated_ursulas)[0].treasure_maps[
|
||||||
digest(enacted_federated_policy.treasure_map.public_id())]
|
digest(enacted_federated_policy.treasure_map.public_id())]
|
||||||
|
|
Loading…
Reference in New Issue