Handle being consumed, validate 'from' field in RPC tx requests.

pull/1092/head
Kieran Prasch 2019-06-20 15:18:08 -07:00
parent dc22bdede0
commit cda052b710
No known key found for this signature in database
GPG Key ID: 199AB839D4125A62
4 changed files with 35 additions and 31 deletions

View File

@ -176,8 +176,10 @@ class Character(Learner):
# Decentralized # Decentralized
# #
if not federated_only: if not federated_only:
if not blockchain:
raise ValueError('No blockchain interface provided to run decentralized mode.')
if not checksum_address: if not checksum_address:
raise ValueError("No checksum_address provided while running in a non-federated mode.") raise ValueError("No checksum_address provided to run in decentralized mode.")
else: else:
self._checksum_address = checksum_address # TODO: Check that this matches TransactingPower self._checksum_address = checksum_address # TODO: Check that this matches TransactingPower
# #

View File

@ -895,11 +895,8 @@ class Ursula(Teacher, Character, Worker):
if not federated_only: if not federated_only:
# Access staking node via node's transacting keys # Access staking node via node's transacting keys
transacting_power = TransactingPower(account=self.checksum_address, transacting_power = TransactingPower(account=worker_address, device=device, blockchain=self.blockchain)
device=device, self._crypto_power.consume_power_up(transacting_power, client_password)
password=client_password, # FIXME: password from somewhere
blockchain=self.blockchain)
self._crypto_power.consume_power_up(transacting_power)
# Use blockchain power to substantiate stamp # Use blockchain power to substantiate stamp
self.substantiate_stamp(client_password=password) self.substantiate_stamp(client_password=password)

View File

@ -127,27 +127,24 @@ class TransactingPower(CryptoPowerUp):
def is_unlocked(self): def is_unlocked(self):
return self.__unlocked return self.__unlocked
def activate(self, password: str): def activate(self, password: str = None):
self.blockchain.connect() """Be Consumed"""
self.unlock_account(password=password) self.blockchain.connect() # Connect
self.blockchain.transacting_power = self self.unlock_account(password=password) # Unlock
self.__password = None self.blockchain.transacting_power = self # Attach
self.__password = None # Discard
def lock_account(self): def lock_account(self):
if self.client: if self.device is not NO_STAKING_DEVICE:
self.client.lock_account(address=self.account)
elif self.device:
# TODO: Implement TrustedDevice # TODO: Implement TrustedDevice
raise NotImplementedError _result = self.device.lock()
else:
_result = self.client.lock_account(address=self.account)
self.__unlocked = False self.__unlocked = False
def unlock_account(self, password: str = None): 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 self.device is not NO_STAKING_DEVICE: if self.device is not NO_STAKING_DEVICE:
# TODO: Embed in TrustedDevice
_hd_path = self.device.get_address_path(checksum_address=self.account) _hd_path = self.device.get_address_path(checksum_address=self.account)
ping = 'PING|PONG' ping = 'PING|PONG'
pong = self.device.client.ping(ping) # TODO: Use pin protection? pong = self.device.client.ping(ping) # TODO: Use pin protection?
@ -168,29 +165,34 @@ class TransactingPower(CryptoPowerUp):
# HW Signer # HW Signer
if self.device is not NO_STAKING_DEVICE: if self.device is not NO_STAKING_DEVICE:
# TODO: Use a common message signature type from clients and devices
signature = self.device.sign_message(checksum_address=self.account, message=message) signature = self.device.sign_message(checksum_address=self.account, message=message)
signature = signature.signature # TODO: Use a common type from clients and devices signature = signature.signature
# Web3 Signer # Web3 Signer
else: else:
signature = self.client.sign_message(account=self.account, message=message) signature = self.client.sign_message(account=self.account, message=message)
return signature return signature
def sign_transaction(self, unsigned_transaction: dict) -> HexBytes: def sign_transaction(self, unsigned_transaction: dict) -> HexBytes:
"""
Signs the transaction with the private key of the TransactingPower.
"""
if not self.is_unlocked: if not self.is_unlocked:
raise PowerUpError("Failed to unlock account {}".format(self.account)) 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=self.account)
# Web3 Signer
else:
# Note: This check is also performed client-side. # Note: This check is also performed client-side.
sender_address = unsigned_transaction['from'] sender_address = unsigned_transaction['from']
if sender_address != self.account: if sender_address != self.account:
raise PowerUpError(f"'from' field must match key's {self.account}, but it was {sender_address}") raise PowerUpError(f"'from' field must match key's {self.account}, but it was {sender_address}")
# HW Signer
if self.device is not NO_STAKING_DEVICE:
# TODO: Use a common tx_sign return type from clients and devices
signed_raw_transaction = self.device.sign_eth_transaction(unsigned_transaction=unsigned_transaction,
checksum_address=self.account)
# Web3 Signer
else:
signed_raw_transaction = self.blockchain.client.sign_transaction(transaction=unsigned_transaction, signed_raw_transaction = self.blockchain.client.sign_transaction(transaction=unsigned_transaction,
account=self.account) account=self.account)
return signed_raw_transaction return signed_raw_transaction

View File

@ -29,6 +29,7 @@ from nucypher.crypto.powers import (CryptoPower,
NoSigningPower, NoSigningPower,
TransactingPower, TransactingPower,
PowerUpError) PowerUpError)
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
""" """
Chapter 1: SIGNING Chapter 1: SIGNING
@ -123,15 +124,17 @@ def test_character_client_transacting_power(testerchain, agency):
sig_pubkey = sig_privkey.public_key sig_pubkey = sig_privkey.public_key
signer = Character(is_me=True, blockchain=testerchain, checksum_address=eth_address) signer = Character(is_me=True, blockchain=testerchain, checksum_address=eth_address)
signer._crypto_power.consume_power_up(TransactingPower(blockchain=testerchain, account=eth_address)) transacting_power = TransactingPower(blockchain=testerchain, account=eth_address)
signer._crypto_power.consume_power_up(transacting_power)
power = signer._crypto_power.power_ups(TransactingPower) power = signer._crypto_power.power_ups(TransactingPower)
power.lock_account()
# Test a signature without unlocking the account # Test a signature without unlocking the account
with pytest.raises(PowerUpError): with pytest.raises(PowerUpError):
power.sign_message(message=b'test', checksum_address=eth_address) power.sign_message(message=b'test')
power.unlock_account(checksum_address=eth_address) power.unlock_account(password=INSECURE_DEVELOPMENT_PASSWORD)
data_to_sign = b'What does Ursula look like?!?' data_to_sign = b'What does Ursula look like?!?'
sig = power.sign_message(message=data_to_sign) sig = power.sign_message(message=data_to_sign)
@ -146,7 +149,7 @@ def test_character_client_transacting_power(testerchain, agency):
assert is_verified is False assert is_verified is False
# Test lockAccount call # Test lockAccount call
power.lock_account(checksum_address=eth_address) power.lock_account()
# Test a signature without unlocking the account # Test a signature without unlocking the account
with pytest.raises(PowerUpError): with pytest.raises(PowerUpError):