mirror of https://github.com/nucypher/nucypher.git
Powerups can be 'activated' - Returning to a single-account-based TransactingPower.
parent
b9fa6310be
commit
e9a3fe7878
|
@ -170,12 +170,6 @@ class Deployer(NucypherTokenActor):
|
|||
deployer_address=self.deployer_address)
|
||||
return r
|
||||
|
||||
@classmethod
|
||||
def from_blockchain(cls, provider_uri: str, registry=None, *args, **kwargs):
|
||||
blockchain = BlockchainInterface.connect(provider_uri=provider_uri, registry=registry)
|
||||
instance = cls(blockchain=blockchain, *args, **kwargs)
|
||||
return instance
|
||||
|
||||
@property
|
||||
def deployer_address(self):
|
||||
return self.blockchain.deployer_address
|
||||
|
|
|
@ -49,7 +49,7 @@ from nucypher.blockchain.eth.providers import (
|
|||
)
|
||||
from nucypher.blockchain.eth.registry import EthereumContractRegistry
|
||||
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
|
||||
from nucypher.crypto.powers import BlockchainPower
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
|
||||
Web3Providers = Union[IPCProvider, WebsocketProvider, HTTPProvider, EthereumTester]
|
||||
|
||||
|
@ -86,7 +86,7 @@ class BlockchainInterface:
|
|||
sync_now: bool = True,
|
||||
provider_process: NuCypherGethProcess = NO_PROVIDER_PROCESS,
|
||||
provider_uri: str = NO_BLOCKCHAIN_CONNECTION,
|
||||
transacting_power: BlockchainPower = READ_ONLY_INTERFACE,
|
||||
transacting_power: TransactingPower = READ_ONLY_INTERFACE,
|
||||
provider: Web3Providers = NO_BLOCKCHAIN_CONNECTION,
|
||||
registry: EthereumContractRegistry = None,
|
||||
fetch_registry: bool = True):
|
||||
|
@ -164,10 +164,10 @@ class BlockchainInterface:
|
|||
self.transacting_power = transacting_power
|
||||
self.registry = registry
|
||||
|
||||
self.connect(provider=provider,
|
||||
provider_uri=provider_uri,
|
||||
fetch_registry=fetch_registry,
|
||||
sync_now=sync_now)
|
||||
# self.connect(provider=provider,
|
||||
# provider_uri=provider_uri,
|
||||
# fetch_registry=fetch_registry,
|
||||
# sync_now=sync_now)
|
||||
|
||||
BlockchainInterface._instance = self
|
||||
|
||||
|
@ -208,21 +208,18 @@ class BlockchainInterface:
|
|||
self.log.debug('Injecting POA middleware at layer 0')
|
||||
self.client.inject_middleware(geth_poa_middleware, layer=0)
|
||||
|
||||
def connect(self,
|
||||
provider: Web3Providers = None,
|
||||
provider_uri: str = None,
|
||||
fetch_registry: bool = True,
|
||||
sync_now: bool = True):
|
||||
def connect(self, fetch_registry: bool = True, sync_now: bool = True):
|
||||
|
||||
# Spawn child process
|
||||
if self._provider_process:
|
||||
self._provider_process.start()
|
||||
provider_uri = self._provider_process.provider_uri(scheme='file')
|
||||
else:
|
||||
self.log.info(f"Using external Web3 Provider '{provider_uri}'")
|
||||
provider_uri = self.provider_uri
|
||||
self.log.info(f"Using external Web3 Provider '{self.provider_uri}'")
|
||||
|
||||
# Attach Provider
|
||||
self._attach_provider(provider=provider, provider_uri=provider_uri)
|
||||
self._attach_provider(provider=self._provider, provider_uri=provider_uri)
|
||||
self.log.info("Connecting to {}".format(self.provider_uri))
|
||||
if self._provider is NO_BLOCKCHAIN_CONNECTION:
|
||||
raise self.NoProvider("There are no configured blockchain providers")
|
||||
|
|
|
@ -91,6 +91,7 @@ class Alice(Character, PolicyAuthor):
|
|||
controller=True,
|
||||
policy_agent=None,
|
||||
device = NO_STAKING_DEVICE,
|
||||
client_password: str = None,
|
||||
*args, **kwargs) -> None:
|
||||
|
||||
#
|
||||
|
@ -120,14 +121,16 @@ class Alice(Character, PolicyAuthor):
|
|||
*args, **kwargs)
|
||||
|
||||
if is_me and not federated_only: # TODO: #289
|
||||
transacting_power = TransactingPower(account=self.checksum_address,
|
||||
device=device,
|
||||
blockchain=self.blockchain)
|
||||
self._crypto_power.consume_power_up(transacting_power, password=client_password)
|
||||
|
||||
PolicyAuthor.__init__(self,
|
||||
blockchain=self.blockchain,
|
||||
policy_agent=policy_agent,
|
||||
checksum_address=checksum_address)
|
||||
|
||||
transacting_power = TransactingPower(blockchain=self.blockchain, account=self.checksum_address)
|
||||
self._crypto_power.consume_power_up(transacting_power)
|
||||
|
||||
if is_me and controller:
|
||||
self.controller = self._controller_class(alice=self)
|
||||
|
||||
|
@ -843,6 +846,7 @@ class Ursula(Teacher, Character, Worker):
|
|||
stake_tracker: StakeTracker = None,
|
||||
staking_agent: StakingEscrowAgent = None,
|
||||
device = NO_STAKING_DEVICE,
|
||||
client_password: str = None,
|
||||
|
||||
# Character
|
||||
password: str = None,
|
||||
|
@ -889,6 +893,17 @@ class Ursula(Teacher, Character, Worker):
|
|||
# Ursula is a Decentralized Worker
|
||||
#
|
||||
if not federated_only:
|
||||
|
||||
# Access staking node via node's transacting keys
|
||||
transacting_power = TransactingPower(account=self.checksum_address,
|
||||
device=device,
|
||||
password=client_password, # FIXME: password from somewhere
|
||||
blockchain=self.blockchain)
|
||||
self._crypto_power.consume_power_up(transacting_power)
|
||||
|
||||
# Use blockchain power to substantiate stamp
|
||||
self.substantiate_stamp(client_password=password)
|
||||
|
||||
Worker.__init__(self,
|
||||
is_me=is_me,
|
||||
blockchain=self.blockchain,
|
||||
|
@ -896,11 +911,6 @@ class Ursula(Teacher, Character, Worker):
|
|||
worker_address=worker_address,
|
||||
stake_tracker=stake_tracker)
|
||||
|
||||
# Access to worker's ETH client via node's transacting keys
|
||||
transacting_power = TransactingPower(blockchain=self.blockchain, account=worker_address)
|
||||
self._crypto_power.consume_power_up(transacting_power)
|
||||
self.substantiate_stamp(client_password=password) # TODO: Use PowerUp / Derive from keyring
|
||||
|
||||
#
|
||||
# ProxyRESTServer and TLSHostingPower #
|
||||
#
|
||||
|
|
|
@ -193,6 +193,7 @@ class CharacterConfiguration(BaseConfiguration):
|
|||
sync_now=sync_now)
|
||||
|
||||
# Read Ethereum Node Keyring
|
||||
self.blockchain.connect(sync_now=sync_now)
|
||||
self.accounts = self.blockchain.client.accounts
|
||||
|
||||
def connect_to_contracts(self) -> None:
|
||||
|
|
|
@ -62,9 +62,10 @@ class CryptoPower(object):
|
|||
else:
|
||||
return True
|
||||
|
||||
def consume_power_up(self, power_up):
|
||||
def consume_power_up(self, power_up, *args, **kwargs):
|
||||
if isinstance(power_up, CryptoPowerUp):
|
||||
power_up_class = power_up.__class__
|
||||
power_up.activate(*args, **kwargs)
|
||||
power_up_instance = power_up
|
||||
elif CryptoPowerUp in inspect.getmro(power_up):
|
||||
power_up_class = power_up
|
||||
|
@ -85,38 +86,66 @@ class CryptoPower(object):
|
|||
raise power_up_class.not_found_error
|
||||
|
||||
|
||||
class CryptoPowerUp(object):
|
||||
class CryptoPowerUp:
|
||||
"""
|
||||
Gives you MORE CryptoPower!
|
||||
"""
|
||||
confers_public_key = False
|
||||
|
||||
def activate(self, *args, **kwargs):
|
||||
return
|
||||
|
||||
|
||||
class TransactingPower(CryptoPowerUp):
|
||||
"""
|
||||
Allows for transacting on a Blockchain via web3 backend.
|
||||
"""
|
||||
not_found_error = NoTransactingPower
|
||||
__accounts = {}
|
||||
|
||||
def __init__(self, blockchain: 'Blockchain', account: str, device = NO_STAKING_DEVICE):
|
||||
def __init__(self,
|
||||
blockchain,
|
||||
account: str,
|
||||
password: str = None,
|
||||
device=NO_STAKING_DEVICE):
|
||||
"""
|
||||
# TODO: TrustedDevice Integration
|
||||
Instantiates a TransactingPower for the given checksum_address.
|
||||
"""
|
||||
self.blockchain = blockchain
|
||||
self.client = self.blockchain.client
|
||||
self.account = account
|
||||
self.device = device
|
||||
self.is_unlocked = False
|
||||
if password and (device is not NO_STAKING_DEVICE):
|
||||
raise ValueError(f"Cannot create a {self.__class__.__name__} with both a client and an device signer.")
|
||||
|
||||
def unlock_account(self, password: str):
|
||||
self.blockchain = blockchain
|
||||
|
||||
self.account = account
|
||||
self.client = self.blockchain.client
|
||||
self.device = device
|
||||
|
||||
self.__password = password
|
||||
self.__unlocked = False
|
||||
self.unlock_account()
|
||||
|
||||
@property
|
||||
def is_unlocked(self):
|
||||
return self.__unlocked
|
||||
|
||||
def activate(self, password: str):
|
||||
self.blockchain.connect()
|
||||
self.unlock_account(password=password)
|
||||
self.blockchain.transacting_power = self
|
||||
self.__password = None
|
||||
|
||||
def lock_account(self):
|
||||
if self.client:
|
||||
self.client.lock_account(address=self.account)
|
||||
elif self.device:
|
||||
# TODO: Implement TrustedDevice
|
||||
raise NotImplementedError
|
||||
self.__unlocked = False
|
||||
|
||||
def unlock_account(self, password: str = None):
|
||||
"""
|
||||
Unlocks the account for the specified duration. If no duration is
|
||||
provided, it will remain unlocked indefinitely.
|
||||
"""
|
||||
if not self.is_unlocked:
|
||||
raise PowerUpError("Failed to unlock account {}".format(self.account))
|
||||
|
||||
if self.device is not NO_STAKING_DEVICE:
|
||||
_hd_path = self.device.get_address_path(checksum_address=self.account)
|
||||
|
@ -125,17 +154,15 @@ class TransactingPower(CryptoPowerUp):
|
|||
if not ping == pong:
|
||||
raise self.device.NoDeviceDetected
|
||||
unlocked = True
|
||||
|
||||
else:
|
||||
unlocked = self.client.unlock_account(address=self.account, password=password)
|
||||
|
||||
self.is_unlocked = unlocked
|
||||
self.__unlocked = unlocked
|
||||
|
||||
def sign_message(self, message: bytes) -> bytes:
|
||||
"""
|
||||
Signs the message with the private key of the TransactingPower.
|
||||
"""
|
||||
|
||||
if not self.is_unlocked:
|
||||
raise PowerUpError("Failed to unlock account {}".format(self.account))
|
||||
|
||||
|
@ -150,17 +177,17 @@ class TransactingPower(CryptoPowerUp):
|
|||
|
||||
return signature
|
||||
|
||||
def sign_transaction(self, checksum_address: str, unsigned_transaction: dict) -> HexBytes:
|
||||
if not self.__accounts.get(checksum_address, False):
|
||||
raise PowerUpError("Account is locked.")
|
||||
def sign_transaction(self, unsigned_transaction: dict) -> HexBytes:
|
||||
if not self.is_unlocked:
|
||||
raise PowerUpError("Failed to unlock account {}".format(self.account))
|
||||
|
||||
# HW Signer
|
||||
if self.device is not NO_STAKING_DEVICE:
|
||||
signed_raw_transaction = self.device.sign_eth_transaction(unsigned_transaction=unsigned_transaction,
|
||||
checksum_address=checksum_address)
|
||||
checksum_address=self.account)
|
||||
# Web3 Signer
|
||||
else:
|
||||
# This check is also performed client-side.
|
||||
# Note: This check is also performed client-side.
|
||||
sender_address = unsigned_transaction['from']
|
||||
if sender_address != self.account:
|
||||
raise PowerUpError(f"'from' field must match key's {self.account}, but it was {sender_address}")
|
||||
|
|
|
@ -32,7 +32,7 @@ from nucypher.blockchain.eth.sol.compile import SolidityCompiler
|
|||
from nucypher.blockchain.eth.token import NU
|
||||
from nucypher.blockchain.eth.utils import epoch_to_period
|
||||
from nucypher.config.constants import BASE_DIR
|
||||
from nucypher.crypto.powers import BlockchainPower
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.utilities.sandbox.constants import (
|
||||
NUMBER_OF_ETH_TEST_ACCOUNTS,
|
||||
NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS,
|
||||
|
@ -110,12 +110,13 @@ class TesterBlockchain(BlockchainDeployerInterface):
|
|||
*args, **kwargs)
|
||||
|
||||
self.log = Logger("test-blockchain")
|
||||
self.connect()
|
||||
|
||||
# Generate additional ethereum accounts for testing
|
||||
population = test_accounts
|
||||
enough_accounts = len(self.w3.eth.accounts) >= population
|
||||
enough_accounts = len(self.client.accounts) >= population
|
||||
if not enough_accounts:
|
||||
accounts_to_make = population - len(self.w3.eth.accounts)
|
||||
accounts_to_make = population - len(self.client.accounts)
|
||||
self.__generate_insecure_unlocked_accounts(quantity=accounts_to_make)
|
||||
assert test_accounts == len(self.w3.eth.accounts)
|
||||
|
||||
|
@ -212,7 +213,7 @@ class TesterBlockchain(BlockchainDeployerInterface):
|
|||
"""For use with metric testing scripts"""
|
||||
|
||||
testerchain = cls(compiler=SolidityCompiler())
|
||||
power = BlockchainPower(blockchain=testerchain, account=testerchain.client.etherbase)
|
||||
power = TransactingPower(blockchain=testerchain, account=testerchain.etherbase_account)
|
||||
power.unlock_account(password=INSECURE_DEVELOPMENT_PASSWORD)
|
||||
testerchain.transacting_power = power
|
||||
|
||||
|
|
|
@ -118,8 +118,8 @@ def test_anybody_can_verify():
|
|||
|
||||
def test_character_client_transacting_power(testerchain, agency):
|
||||
# TODO: Handle multiple providers
|
||||
eth_address = testerchain.interface.w3.eth.accounts[0]
|
||||
sig_privkey = testerchain.interface.provider.ethereum_tester.backend._key_lookup[eth_utils.to_canonical_address(eth_address)]
|
||||
eth_address = testerchain.etherbase_account
|
||||
sig_privkey = testerchain.provider.ethereum_tester.backend._key_lookup[eth_utils.to_canonical_address(eth_address)]
|
||||
sig_pubkey = sig_privkey.public_key
|
||||
|
||||
signer = Character(is_me=True, blockchain=testerchain, checksum_address=eth_address)
|
||||
|
|
|
@ -39,15 +39,12 @@ from nucypher.blockchain.eth.deployers import (NucypherTokenDeployer,
|
|||
PolicyManagerDeployer,
|
||||
DispatcherDeployer,
|
||||
AdjudicatorDeployer)
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface
|
||||
from nucypher.blockchain.eth.registry import InMemoryEthereumContractRegistry
|
||||
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
|
||||
from nucypher.blockchain.eth.token import NU
|
||||
from nucypher.characters.lawful import Enrico, Bob
|
||||
from nucypher.config.characters import UrsulaConfiguration, AliceConfiguration, BobConfiguration
|
||||
from nucypher.config.constants import BASE_DIR
|
||||
from nucypher.config.node import CharacterConfiguration
|
||||
from nucypher.crypto.powers import BlockchainPower
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.crypto.utils import canonical_address_from_umbral_key
|
||||
from nucypher.keystore import keystore
|
||||
from nucypher.keystore.db import Base
|
||||
|
@ -59,8 +56,8 @@ from nucypher.utilities.sandbox.constants import (DEVELOPMENT_ETH_AIRDROP_AMOUNT
|
|||
MOCK_URSULA_STARTING_PORT,
|
||||
NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK,
|
||||
TEMPORARY_DOMAIN,
|
||||
TEST_PROVIDER_URI
|
||||
)
|
||||
TEST_PROVIDER_URI,
|
||||
INSECURE_DEVELOPMENT_PASSWORD)
|
||||
from nucypher.utilities.sandbox.middleware import MockRestMiddleware
|
||||
from nucypher.utilities.sandbox.policy import generate_random_label
|
||||
from nucypher.utilities.sandbox.ursula import (make_decentralized_ursulas,
|
||||
|
@ -371,10 +368,12 @@ def testerchain():
|
|||
# Create the blockchain
|
||||
testerchain = TesterBlockchain(eth_airdrop=True, free_transactions=True)
|
||||
|
||||
# TODO: TransactingPower
|
||||
# Mock TransactingPower Consumption
|
||||
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=testerchain.etherbase_account)
|
||||
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
|
||||
password=INSECURE_DEVELOPMENT_PASSWORD,
|
||||
account=testerchain.etherbase_account)
|
||||
testerchain.deployer_address = testerchain.etherbase_account
|
||||
testerchain.transacting_power.unlock_account()
|
||||
yield testerchain
|
||||
testerchain.disconnect()
|
||||
|
||||
|
@ -430,8 +429,8 @@ def stakers(agency, token_economics):
|
|||
blockchain = token_agent.blockchain
|
||||
|
||||
# Mock Powerup consumption (Deployer)
|
||||
blockchain.transacting_power = BlockchainPower(blockchain=blockchain,
|
||||
account=blockchain.etherbase_account)
|
||||
blockchain.transacting_power = TransactingPower(blockchain=blockchain,
|
||||
account=blockchain.etherbase_account)
|
||||
|
||||
token_airdrop(origin=blockchain.etherbase_account,
|
||||
addresses=blockchain.stakers_accounts,
|
||||
|
@ -442,9 +441,10 @@ def stakers(agency, token_economics):
|
|||
for index, account in enumerate(blockchain.stakers_accounts):
|
||||
staker = Staker(is_me=True, checksum_address=account, blockchain=blockchain)
|
||||
|
||||
# Mock TransactingPower consumption (Ursula-Staker)
|
||||
staker.blockchain.transacting_power = BlockchainPower(blockchain=staker.blockchain,
|
||||
account=staker.checksum_address)
|
||||
# Mock TransactingPower consumption
|
||||
staker.blockchain.transacting_power = TransactingPower(blockchain=blockchain,
|
||||
account=account,
|
||||
password=INSECURE_DEVELOPMENT_PASSWORD)
|
||||
|
||||
min_stake, balance = token_economics.minimum_allowed_locked, staker.token_balance
|
||||
amount = random.randint(min_stake, balance)
|
||||
|
|
Loading…
Reference in New Issue