Hack and Slash a Clef POC.

pull/1664/head
Kieran Prasch 2020-01-28 11:36:24 -08:00
parent e6df7d0e61
commit 78b7e5a2c0
No known key found for this signature in database
GPG Key ID: 199AB839D4125A62
4 changed files with 92 additions and 32 deletions

View File

@ -36,7 +36,7 @@ from constant_sorrow.constants import (
from eth_tester.exceptions import TransactionFailed
from eth_utils import keccak, is_checksum_address, to_checksum_address
from twisted.logger import Logger
from web3 import Web3
from web3 import Web3, IPCProvider
from web3.contract import ContractFunction
from nucypher.blockchain.economics import StandardTokenEconomics, EconomicsFactory, BaseEconomics
@ -48,8 +48,11 @@ from nucypher.blockchain.eth.agents import (
ContractAgency,
PreallocationEscrowAgent,
MultiSigAgent,
WorkLockAgent)
from nucypher.blockchain.eth.decorators import validate_checksum_address, only_me, save_receipt
WorkLockAgent
)
from nucypher.blockchain.eth.clients import ClefClient
from nucypher.blockchain.eth.decorators import only_me, save_receipt
from nucypher.blockchain.eth.decorators import validate_checksum_address
from nucypher.blockchain.eth.deployers import (
NucypherTokenDeployer,
StakingEscrowDeployer,
@ -1478,10 +1481,16 @@ class StakeHolder(Staker):
def __init__(self,
registry: BaseContractRegistry,
client_addresses: set = None,
keyfiles: List[str] = None):
keyfiles: List[str] = None,
signer=None):
# Wallet
self.__keyfiles = keyfiles
if signer:
provider = IPCProvider(signer)
w3 = Web3(provider=provider)
signer = ClefClient(w3=w3)
self.__signer = signer
self.__keyfiles = keyfiles or list()
self.__local_accounts = dict()
self.__client_accounts = set() # Note: Account index is meaningless here
self.__transacting_powers = dict()
@ -1504,10 +1513,9 @@ class StakeHolder(Staker):
return self.blockchain.transacting_power.account
def __get_accounts(self) -> None:
# chain_name = self.blockchain.client.chain_name.lower()
# keystore = f'{os.path.expanduser("~")}/.ethereum/{chain_name}/keystore'
# keyfiles = os.listdir(keystore)
# for keyfile in keyfiles:
if self.__signer:
signer_accounts = self.__signer.accounts()
self.__client_accounts.update(signer_accounts)
for keyfile in self.__keyfiles:
try:
account = to_checksum_address(keyfile.split('--')[-1])
@ -1531,7 +1539,10 @@ class StakeHolder(Staker):
transacting_power = self.__transacting_powers[checksum_address]
except KeyError:
keyfile = self.__local_accounts.get(checksum_address)
transacting_power = TransactingPower(password=password, account=checksum_address, keyfile=keyfile)
transacting_power = TransactingPower(password=password,
account=checksum_address,
client=self.__signer,
keyfile=keyfile)
self.__transacting_powers[checksum_address] = transacting_power
transacting_power.activate(password=password)
@ -1553,6 +1564,7 @@ class StakeHolder(Staker):
initial_address: str = None,
checksum_addresses: set = None,
keyfiles: List[str] = None,
signer: str = None,
password: str = None,
*args, **kwargs):
@ -1564,7 +1576,8 @@ class StakeHolder(Staker):
# Wallet
self.wallet = self.StakingWallet(registry=self.registry,
client_addresses=checksum_addresses,
keyfiles=keyfiles)
keyfiles=keyfiles,
signer=signer)
if initial_address:
# If an initial address was passed,
# it is safe to understand that it has already been used at a higher level.

View File

@ -1,15 +1,15 @@
import json
import os
import shutil
import time
from typing import Union
from typing import Union, List
import maya
import time
from constant_sorrow.constants import NOT_RUNNING, UNKNOWN_DEVELOPMENT_CHAIN_ID
from cytoolz.dicttoolz import dissoc
from eth_account import Account
from eth_account.messages import encode_defunct
from eth_utils import to_canonical_address
from eth_utils import to_canonical_address, to_normalized_address
from eth_utils import to_checksum_address
from geth import LoggingMixin
from geth.accounts import get_accounts, create_new_account
@ -25,7 +25,6 @@ from web3 import Web3
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, DEPLOY_DIR, USER_LOG_DIR
UNKNOWN_DEVELOPMENT_CHAIN_ID.bool_value(True)
@ -71,10 +70,13 @@ class Web3Client:
PARITY = 'Parity'
ALT_PARITY = 'Parity-Ethereum'
GANACHE = 'EthereumJS TestRPC'
ETHEREUM_TESTER = 'EthereumTester' # (PyEVM)
CLEF = 'Clef' # Signer-only
PEERING_TIMEOUT = 30 # seconds
SYNC_TIMEOUT_DURATION = 60 # seconds to wait for various blockchain syncing endeavors
PEERING_TIMEOUT = 30
SYNC_SLEEP_DURATION = 5
SYNC_SLEEP_DURATION = 5 # seconds
class ConnectionNotEstablished(RuntimeError):
pass
@ -124,6 +126,9 @@ class Web3Client:
# Test Clients
cls.GANACHE: GanacheClient,
cls.ETHEREUM_TESTER: EthereumTesterClient,
# Singers
cls.CLEF: ClefClient
}
try:
@ -132,6 +137,9 @@ class Web3Client:
ClientSubclass = clients[node_technology]
except (ValueError, IndexError):
# check if this is a clef signer TODO: move this?
if 'clef' in getattr(w3.provider, 'ipc_path', ''):
return ClefClient(w3=w3)
raise ValueError(f"Invalid client version string. Got '{w3.clientVersion}'")
except KeyError:
@ -355,6 +363,41 @@ class GethClient(Web3Client):
return self.w3.manager.request_blocking("personal_listWallets", [])
class ClefClient:
def __init__(self, w3):
self.w3 = w3
self.log = Logger(self.__class__.__name__)
def is_connected(self):
return True
def accounts(self) -> List[str]:
normalized_addresses = self.w3.manager.request_blocking("account_list", [])
checksum_addresses = [to_checksum_address(addr) for addr in normalized_addresses]
return checksum_addresses
def sign_transaction(self, transaction: dict):
# FIXME: SO LAME
transaction['value'] = self.w3.toHex(transaction['value'])
transaction['gas'] = self.w3.toHex(transaction['gas'])
transaction['gasPrice'] = self.w3.toHex(transaction['gasPrice'])
transaction['chainId'] = self.w3.toHex(transaction['chainId'])
transaction['nonce'] = self.w3.toHex(transaction['nonce'])
transaction['from'] = to_normalized_address(transaction['from'])
signed = self.w3.manager.request_blocking("account_signTransaction", [transaction])
return signed.raw
def sign_message(self, account: str, message: bytes) -> str:
return self.w3.manager.request_blocking("account_signData", [message])
def unlock_account(self) -> bool:
return True
def lock_account(self):
return True
class ParityClient(Web3Client):
@property

View File

@ -150,7 +150,7 @@ class StakerOptions:
self.config_options = config_options
self.staking_address = staking_address
def create_character(self, emitter, config_file, individual_allocation=None, initial_address=None, keyfiles=None):
def create_character(self, emitter, config_file, initial_address=None, *args, **kwargs):
stakeholder_config = self.config_options.create_config(emitter, config_file)
if initial_address is None:
@ -158,8 +158,7 @@ class StakerOptions:
return stakeholder_config.produce(
initial_address=initial_address,
individual_allocation=individual_allocation,
keyfiles=keyfiles
*args, **kwargs
)
def get_blockchain(self):
@ -177,12 +176,13 @@ class TransactingStakerOptions:
__option_name__ = 'transacting_staker_options'
def __init__(self, staker_options, hw_wallet, beneficiary_address, allocation_filepath, keyfile=None):
def __init__(self, staker_options, hw_wallet, beneficiary_address, allocation_filepath, keyfile, signer):
self.staker_options = staker_options
self.hw_wallet = hw_wallet
self.beneficiary_address = beneficiary_address
self.allocation_filepath = allocation_filepath
self.keyfile = keyfile
self.signer = signer
def create_character(self, emitter, config_file):
@ -223,7 +223,8 @@ class TransactingStakerOptions:
config_file,
individual_allocation=individual_allocation,
initial_address=initial_address,
keyfiles=[self.keyfile] # TODO: Accept multiple?
keyfiles=[self.keyfile] if self.keyfile else None, # TODO: Accept multiple?,
signer=self.signer,
)
def get_blockchain(self):
@ -242,8 +243,10 @@ group_transacting_staker_options = group_options(
hw_wallet=option_hw_wallet,
beneficiary_address=click.option('--beneficiary-address', help="Address of a pre-allocation beneficiary", type=EIP55_CHECKSUM_ADDRESS),
allocation_filepath=click.option('--allocation-filepath', help="Path to individual allocation file", type=EXISTING_READABLE_FILE),
keyfile=click.option('--keyfile', default=None, type=EXISTING_READABLE_FILE)
)
keyfile=click.option('--keyfile', default=None, type=EXISTING_READABLE_FILE),
signer=click.option('--signer', default=None, type=EXISTING_READABLE_FILE)
)
@click.group()

View File

@ -116,14 +116,15 @@ class TransactingPower(CryptoPowerUp):
def __init__(self,
account: str,
provider_uri: str = None,
client=None,
password: str = None,
cache: bool = False,
keyfile: str = None):
"""
Instantiates a TransactingPower for the given checksum_address.
"""
self.blockchain = BlockchainInterfaceFactory.get_or_create_interface(provider_uri=provider_uri)
self.blockchain = BlockchainInterfaceFactory.get_interface()
self.__client = client or self.blockchain.client # injectable signer
self.__account = account
self.__password = password
self.__unlocked = False
@ -138,7 +139,7 @@ class TransactingPower(CryptoPowerUp):
return False
try:
# TODO: Temporary fix for #1128 and #1385. It's ugly af, but it works. Move somewhere else?
wallets = self.blockchain.client.wallets
wallets = self.__client.wallets
except AttributeError:
return False
else:
@ -176,7 +177,7 @@ class TransactingPower(CryptoPowerUp):
try:
with open(self.__keyfile) as keyfile:
encrypted_key = keyfile.read()
private_key = self.blockchain.client.w3.eth.account.decrypt(encrypted_key, password)
private_key = self.__client.w3.eth.account.decrypt(encrypted_key, password)
except FileNotFoundError:
raise # TODO
except Exception:
@ -195,7 +196,7 @@ class TransactingPower(CryptoPowerUp):
elif self.is_local:
self.__key = None
else:
self.blockchain.client.lock_account(address=self.account)
self.__client.lock_account(address=self.account)
self.__unlocked = False
return self.__unlocked
@ -206,7 +207,7 @@ class TransactingPower(CryptoPowerUp):
elif self.is_local:
unlocked = self.__import_keyfile(password=password)
else:
if self.blockchain.client is NO_BLOCKCHAIN_CONNECTION:
if self.__client is NO_BLOCKCHAIN_CONNECTION:
raise self.NoBlockchainConnection
unlocked = self.blockchain.client.unlock_account(address=self.account, password=password, duration=duration)
self.__unlocked = unlocked
@ -232,7 +233,7 @@ class TransactingPower(CryptoPowerUp):
signed_transaction = w3.eth.account.sign_transaction(transaction_dict=unsigned_transaction, private_key=self.__key)
signed_raw_transaction = signed_transaction['rawTransaction']
else:
signed_raw_transaction = self.blockchain.client.sign_transaction(transaction=unsigned_transaction)
signed_raw_transaction = self.__client.sign_transaction(transaction=unsigned_transaction)
return signed_raw_transaction
def __enter__(self):