mirror of https://github.com/nucypher/nucypher.git
Hack and Slash a Clef POC.
parent
e6df7d0e61
commit
78b7e5a2c0
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue