diff --git a/nucypher/blockchain/eth/agents.py b/nucypher/blockchain/eth/agents.py index a9b82eb18..fb4759257 100644 --- a/nucypher/blockchain/eth/agents.py +++ b/nucypher/blockchain/eth/agents.py @@ -59,7 +59,7 @@ class EthereumContractAgent: pass def __init__(self, - blockchain: BlockchainInterface = None, + blockchain: BlockchainInterface, contract: Contract = None, transaction_gas: int = None ) -> None: diff --git a/nucypher/blockchain/eth/interfaces.py b/nucypher/blockchain/eth/interfaces.py index b0474cb4d..f7d353885 100644 --- a/nucypher/blockchain/eth/interfaces.py +++ b/nucypher/blockchain/eth/interfaces.py @@ -318,7 +318,7 @@ class BlockchainInterface: # Broadcast # - signed_raw_transaction = self.transacting_power.sign_transaction(unsigned_transaction, sender_address) + signed_raw_transaction = self.transacting_power.sign_transaction(unsigned_transaction) txhash = self.client.w3.eth.sendRawTransaction(signed_raw_transaction) try: diff --git a/nucypher/characters/chaotic.py b/nucypher/characters/chaotic.py index 07b21e594..de33553f3 100644 --- a/nucypher/characters/chaotic.py +++ b/nucypher/characters/chaotic.py @@ -182,9 +182,9 @@ class Felix(Character, NucypherTokenActor): self.db_engine = create_engine(f'sqlite:///{self.db_filepath}', convert_unicode=True) # Blockchain - blockchain_power = BlockchainPower(client=self.blockchain.client) + blockchain_power = BlockchainPower(blockchain=self.blockchain, account=self.checksum_address) self._crypto_power.consume_power_up(blockchain_power) - # blockchain_power.unlock_account(account=self.checksum_address) # TODO + # blockchain_power.unlock_account(password=None) # TODO: TransactingPower self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) self.reserved_addresses = [self.checksum_address, BlockchainInterface.NULL_ADDRESS] diff --git a/nucypher/characters/lawful.py b/nucypher/characters/lawful.py index 774f2edf4..a25149a34 100644 --- a/nucypher/characters/lawful.py +++ b/nucypher/characters/lawful.py @@ -124,9 +124,10 @@ class Alice(Character, PolicyAuthor): policy_agent=policy_agent, checksum_address=checksum_address) - blockchain_power = BlockchainPower(client=self.blockchain.client) + # TODO: #1092 - TransactingPower + blockchain_power = BlockchainPower(blockchain=self.blockchain, account=self.checksum_address) self._crypto_power.consume_power_up(blockchain_power) - self.blockchain.transacting_power = blockchain_power # TODO: #1092 + self.blockchain.transacting_power = blockchain_power # TODO: Embed in Powerups if is_me and controller: self.controller = self._controller_class(alice=self) @@ -839,7 +840,6 @@ class Ursula(Teacher, Character, Worker): checksum_address: str = None, # Staker address worker_address: str = None, stake_tracker: StakeTracker = None, - staking_agent: StakingEscrowAgent = None, # Character password: str = None, @@ -877,7 +877,9 @@ class Ursula(Teacher, Character, Worker): # # Self-Ursula # - if is_me is True: # TODO: 340 + # TODO: Better handle ephemeral staking self ursula <-- Is this still relevant? + + if is_me is True: # TODO: #340 self._stored_treasure_maps = dict() # @@ -892,13 +894,11 @@ class Ursula(Teacher, Character, Worker): stake_tracker=stake_tracker) # Access to worker's ETH client via node's transacting keys - # TODO: Better handle ephemeral staking self ursula <-- Is this still relevant? - blockchain_power = BlockchainPower(client=self.blockchain.client) + # TODO: #1092 - TransactingPower + blockchain_power = BlockchainPower(blockchain=self.blockchain, account=worker_address) self._crypto_power.consume_power_up(blockchain_power) - # TODO: #1092 - - # Use blockchain power to substantiate stamp - self.substantiate_stamp(client_password=password, checksum_address=worker_address) # TODO: Derive from keyring + self.blockchain.transacting_power = blockchain_power # TODO: Embed in powerups + self.substantiate_stamp(client_password=password) # TODO: Use PowerUp / Derive from keyring # # ProxyRESTServer and TLSHostingPower # TODO: Maybe we want _power_ups to be public after all? diff --git a/nucypher/cli/deploy.py b/nucypher/cli/deploy.py index eb3f439e7..a3a9ecaee 100644 --- a/nucypher/cli/deploy.py +++ b/nucypher/cli/deploy.py @@ -108,9 +108,6 @@ def deploy(click_config, fetch_registry=False, sync_now=sync) - # TODO: Integrate with Deployer Actor (Character) - blockchain.transacting_power = BlockchainPower(client=blockchain.client) - # # Deployment Actor # @@ -125,6 +122,9 @@ def deploy(click_config, # Verify Address if not force: click.confirm("Selected {} - Continue?".format(deployer_address), abort=True) + + # TODO: Integrate with Deployer Actor (Character) + blockchain.transacting_power = BlockchainPower(blockchain=blockchain, account=deployer_address) deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address) # Verify ETH Balance diff --git a/nucypher/crypto/powers.py b/nucypher/crypto/powers.py index 96cb3e1aa..3ee275a29 100644 --- a/nucypher/crypto/powers.py +++ b/nucypher/crypto/powers.py @@ -92,39 +92,44 @@ class BlockchainPower(CryptoPowerUp): """ not_found_error = NoBlockchainPower - def __init__(self, client=None, device=None) -> None: + def __init__(self, blockchain: 'Blockchain', account: str, device = None) -> None: """ Instantiates a BlockchainPower for the given account id. """ + self.blockchain = blockchain + self.account = account self.device = device - self.client = client self.is_unlocked = False - def unlock_account(self, account, password: str): + def unlock_account(self, password: str): """ Unlocks the account for the specified duration. If no duration is provided, it will remain unlocked indefinitely. """ - self.is_unlocked = self.client.unlock_account(account, password) + self.is_unlocked = self.blockchain.client.unlock_account(self.account, password) if not self.is_unlocked: - raise PowerUpError("Failed to unlock account {}".format(account)) + raise PowerUpError("Failed to unlock account {}".format(self.account)) - def sign_message(self, message: bytes, account: str) -> bytes: + def sign_message(self, message: bytes) -> bytes: """ Signs the message with the private key of the BlockchainPower. """ if not self.is_unlocked: raise PowerUpError("Account is not unlocked.") - signature = self.client.sign_message(account, message) + signature = self.blockchain.client.sign_message(account=self.account, message=message) return signature - def sign_transaction(self, unsigned_transaction: dict, sender_address: str): + def sign_transaction(self, unsigned_transaction: dict): if self.device: - assert False - elif self.client: - signed_raw_transaction = self.client.sign_transaction(transaction=unsigned_transaction, - account=sender_address) - return signed_raw_transaction + # TODO: Implement TrustedDevice + raise NotImplementedError + + 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}") + + signed_transaction = self.blockchain.client.sign_transaction(transaction=unsigned_transaction, account=self.account) + return signed_transaction class KeyPairBasedPower(CryptoPowerUp): diff --git a/nucypher/network/nodes.py b/nucypher/network/nodes.py index 5fd051371..9f2554755 100644 --- a/nucypher/network/nodes.py +++ b/nucypher/network/nodes.py @@ -913,7 +913,8 @@ class Teacher: self.__worker_address = None if substantiate_immediately: - self.substantiate_stamp(client_password=password, checksum_address=worker_address) + # TODO: #1091 + self.substantiate_stamp(client_password=password) class InvalidNode(SuspiciousActivity): """Raised when a node has an invalid characteristic - stamp, interface, or address.""" @@ -1146,12 +1147,13 @@ class Teacher: signature=self.decentralized_identity_evidence) return self.__worker_address - def substantiate_stamp(self, client_password: str, checksum_address: str): + def substantiate_stamp(self, client_password: str): + # TODO: #1092 - TransactingPower blockchain_power = self._crypto_power.power_ups(BlockchainPower) - blockchain_power.unlock_account(password=client_password, account=checksum_address) # TODO: #349 - signature = blockchain_power.sign_message(message=bytes(self.stamp), account=checksum_address) + blockchain_power.unlock_account(password=client_password) # TODO: #349 + signature = blockchain_power.sign_message(message=bytes(self.stamp)) self.__decentralized_identity_evidence = signature - self.__worker_address = checksum_address + self.__worker_address = blockchain_power.account # # Interface diff --git a/nucypher/utilities/sandbox/blockchain.py b/nucypher/utilities/sandbox/blockchain.py index 5ad38e193..7f8f0e8fa 100644 --- a/nucypher/utilities/sandbox/blockchain.py +++ b/nucypher/utilities/sandbox/blockchain.py @@ -212,8 +212,8 @@ class TesterBlockchain(BlockchainDeployerInterface): """For use with metric testing scripts""" testerchain = cls(compiler=SolidityCompiler()) - power = BlockchainPower(client=testerchain.client) - power.unlock_account(account=testerchain.client.etherbase, password=INSECURE_DEVELOPMENT_PASSWORD) + power = BlockchainPower(blockchain=testerchain, account=testerchain.client.etherbase) + power.unlock_account(password=INSECURE_DEVELOPMENT_PASSWORD) testerchain.transacting_power = power origin = testerchain.client.etherbase diff --git a/tests/blockchain/eth/entities/actors/test_deployer.py b/tests/blockchain/eth/entities/actors/test_deployer.py index 02aa54260..c144ed593 100644 --- a/tests/blockchain/eth/entities/actors/test_deployer.py +++ b/tests/blockchain/eth/entities/actors/test_deployer.py @@ -47,7 +47,8 @@ def test_rapid_deployment(token_economics): test_accounts=4, compiler=compiler) - blockchain.transacting_power = BlockchainPower(client=blockchain.client) + # TODO: #1092 - TransactingPower + blockchain.transacting_power = BlockchainPower(blockchain=blockchain, account=blockchain.etherbase_account) deployer_address = blockchain.etherbase_account deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address) diff --git a/tests/characters/test_crypto_characters_and_their_powers.py b/tests/characters/test_crypto_characters_and_their_powers.py index 4137dec16..46d7dc385 100644 --- a/tests/characters/test_crypto_characters_and_their_powers.py +++ b/tests/characters/test_crypto_characters_and_their_powers.py @@ -123,7 +123,7 @@ def test_character_blockchain_power(testerchain, agency): sig_privkey = testerchain.provider.ethereum_tester.backend._key_lookup[canonical_address] signer = Character(is_me=True, blockchain=testerchain, checksum_address=eth_address) - signer._crypto_power.consume_power_up(BlockchainPower(testerchain.client, eth_address)) + signer._crypto_power.consume_power_up(BlockchainPower(blockchain=testerchain, account=eth_address)) # Due to testing backend, the account is already unlocked. power = signer._crypto_power.power_ups(BlockchainPower) @@ -131,7 +131,7 @@ def test_character_blockchain_power(testerchain, agency): # power.unlock_account('this-is-not-a-secure-password') data_to_sign = b'What does Ursula look like?!?' - sig = power.sign_message(message=data_to_sign, account=eth_address) + sig = power.sign_message(message=data_to_sign) is_verified = verify_eip_191(address=eth_address, message=data_to_sign, signature=sig) assert is_verified is True @@ -145,7 +145,7 @@ def test_character_blockchain_power(testerchain, agency): # Test a signature without unlocking the account power.is_unlocked = False with pytest.raises(PowerUpError): - power.sign_message(message=b'test', account=eth_address) + power.sign_message(message=b'test') # Test lockAccount call del power diff --git a/tests/cli/test_deploy.py b/tests/cli/test_deploy.py index b8b3acf0d..f9e8518e9 100644 --- a/tests/cli/test_deploy.py +++ b/tests/cli/test_deploy.py @@ -44,7 +44,7 @@ def make_testerchain(): # Set the deployer address from a freshly created test account testerchain.deployer_address = testerchain.etherbase_account - testerchain.transacting_power = BlockchainPower(client=testerchain.client) + testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=testerchain.etherbase_account) return testerchain diff --git a/tests/fixtures.py b/tests/fixtures.py index 33a74898d..3cca60887 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -370,7 +370,10 @@ def testerchain(): """ # Create the blockchain testerchain = TesterBlockchain(eth_airdrop=True, free_transactions=True) - testerchain.transacting_power = BlockchainPower(client=testerchain.client) + + # TODO: TransactingPower + # Mock TransactingPower Consumption + testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=testerchain.etherbase_account) testerchain.deployer_address = testerchain.etherbase_account yield testerchain testerchain.disconnect() @@ -426,6 +429,10 @@ def stakers(agency, token_economics): for index, account in enumerate(blockchain.stakers_accounts): staker = Staker(is_me=True, checksum_address=account, blockchain=blockchain) + # TODO: #1092 - TransactingPower + # Mock TransactingPower consumption + staker.blockchain.transacting_power = BlockchainPower(blockchain=staker.blockchain, account=staker.checksum_address) + min_stake, balance = token_economics.minimum_allowed_locked, staker.token_balance amount = random.randint(min_stake, balance)