mirror of https://github.com/nucypher/nucypher.git
220 lines
7.2 KiB
Python
Executable File
220 lines
7.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
"""
|
|
WARNING: This script makes automatic transactions.
|
|
Do not use this script unless you know what you
|
|
are doing and intend to spend ETH measuring live
|
|
policy availability.
|
|
"""
|
|
|
|
import datetime
|
|
import os
|
|
import shutil
|
|
import time
|
|
from pathlib import Path
|
|
from typing import List, Optional, Set, Tuple
|
|
|
|
import maya
|
|
from eth_typing.evm import ChecksumAddress
|
|
from nucypher_core.umbral import SecretKey
|
|
from web3 import Web3
|
|
from web3.types import Wei
|
|
|
|
from nucypher.blockchain.eth.domains import LYNX
|
|
from nucypher.blockchain.eth.signers import Signer
|
|
from nucypher.characters.lawful import Alice, Bob, Ursula
|
|
from nucypher.config.characters import AliceConfiguration
|
|
from nucypher.network.nodes import TEACHER_NODES
|
|
from nucypher.policy.payment import SubscriptionManagerPayment
|
|
from nucypher.policy.policies import Policy
|
|
from nucypher.utilities.logging import GlobalLoggerSettings
|
|
|
|
# Signer Configuration
|
|
# In order to use this script, you must configure a wallet for alice
|
|
ADDRESS_ENVVAR: str = 'NUCYPHER_GRANT_METRICS_ADDRESS'
|
|
PASSWORD_ENVVAR: str = 'NUCYPHER_GRANT_METRICS_PASSWORD'
|
|
SIGNER_ENVVAR: str = 'NUCYPHER_GRANT_METRICS_KEYFILE_PATH'
|
|
PROVIDER_ENVVAR: str = 'NUCYPHER_GRANT_METRICS_PROVIDER'
|
|
POLYGON_ENVVAR: str = 'NUCYPHER_GRANT_METRICS_POLYGON_PROVIDER'
|
|
|
|
try:
|
|
ALICE_ADDRESS: ChecksumAddress = os.environ[ADDRESS_ENVVAR]
|
|
SIGNER_PASSWORD: str = os.environ[PASSWORD_ENVVAR]
|
|
SIGNER_URI: str = os.environ[SIGNER_ENVVAR]
|
|
ETHEREUM_PROVIDER_URI: str = os.environ[PROVIDER_ENVVAR]
|
|
POLYGON_PROVIDER_URI: str = os.environ[POLYGON_ENVVAR]
|
|
except KeyError:
|
|
message = f'{ADDRESS_ENVVAR}, ' \
|
|
f'{PROVIDER_ENVVAR}, ' \
|
|
f'{SIGNER_ENVVAR} and ' \
|
|
f'{PASSWORD_ENVVAR} ' \
|
|
f'are required to run grant availability metrics.'
|
|
raise RuntimeError(message)
|
|
|
|
# Alice Configuration
|
|
TACO_DOMAIN: str = str(LYNX) # mainnet
|
|
DEFAULT_SEEDNODE_URIS: List[str] = [
|
|
*TEACHER_NODES[TACO_DOMAIN],
|
|
]
|
|
INSECURE_PASSWORD: str = "METRICS_INSECURE_DEVELOPMENT_PASSWORD"
|
|
TEMP_ALICE_DIR: Path = Path('/', 'tmp', 'grant-metrics')
|
|
|
|
# Policy Parameters
|
|
THRESHOLD: int = 1
|
|
SHARES: int = 1
|
|
RATE: Wei = Web3.to_wei(50, 'gwei')
|
|
DURATION: datetime.timedelta = datetime.timedelta(days=1)
|
|
|
|
# Tuning
|
|
DEFAULT_ITERATIONS = None # `None` will run forever
|
|
SAMPLE_RATE: int = 15 # seconds
|
|
GAS_STRATEGY: str = 'fast'
|
|
MAX_GAS_PRICE: int = 200 # gwei
|
|
LABEL_PREFIX = 'random-metrics-label-'
|
|
def LABEL_SUFFIXER():
|
|
return os.urandom(16).hex()
|
|
HANDPICKED_URSULA_URIS: List[str] = [
|
|
# DEFAULT_SEEDNODE_URIS[0], # uncomment to use the seednode for granting
|
|
]
|
|
|
|
|
|
def make_random_bob():
|
|
"""Generates a random ephemeral Bob instance."""
|
|
bob_verifying_secret = SecretKey.random()
|
|
bob_verifying_key = bob_verifying_secret.public_key()
|
|
decrypting_secret = SecretKey.random()
|
|
decrypting_key = decrypting_secret.public_key()
|
|
bob = Bob.from_public_keys(
|
|
verifying_key=bob_verifying_key, encrypting_key=decrypting_key
|
|
)
|
|
print(f"Created BOB - {bytes(bob.stamp).hex()}")
|
|
return bob
|
|
|
|
|
|
BOB = make_random_bob()
|
|
|
|
|
|
def metric_grant(alice, ursulas: Optional[Set[Ursula]] = None) -> Policy:
|
|
"""Perform a granting operation for metrics collection."""
|
|
label = f'{LABEL_PREFIX}{LABEL_SUFFIXER()}'.encode()
|
|
policy_end_datetime = maya.now() + DURATION
|
|
policy = alice.grant(threshold=THRESHOLD,
|
|
shares=SHARES,
|
|
# ursulas=handpicked_ursulas,
|
|
expiration=policy_end_datetime,
|
|
bob=BOB,
|
|
label=label)
|
|
return policy
|
|
|
|
|
|
def collect(alice: Alice,
|
|
ursulas: Optional[Set[Ursula]] = None,
|
|
iterations: Optional[int] = DEFAULT_ITERATIONS,
|
|
) -> None:
|
|
"""Collects grant success and failure rates."""
|
|
policies, i, success, fail = dict(), 0, 0, 0
|
|
while True:
|
|
print(f"Attempt {i+1} of {iterations if iterations is not None else 'infinite'}")
|
|
start = maya.now()
|
|
try:
|
|
policy = metric_grant(alice=alice, ursulas=ursulas)
|
|
except Exception as e:
|
|
fail += 1
|
|
print(f'GRANT FAIL\n{e}')
|
|
else:
|
|
success += 1
|
|
policies[policy.public_key.to_compressed_bytes().hex()] = policy # track
|
|
print(
|
|
f"PEK:{policy.public_key.to_compressed_bytes().hex()} | {policy.hrac}"
|
|
)
|
|
|
|
# timeit
|
|
end = maya.now()
|
|
delta = end - start
|
|
print(f"Completed in {(delta).total_seconds()} seconds.")
|
|
|
|
# score
|
|
if i+1 != iterations:
|
|
if fail > 0:
|
|
print(f'{fail}/{i+1} ({(fail/(i+1))*100}%) failure rate')
|
|
if success > 0:
|
|
print(f'{success}/{i+1} ({(success/(i+1))*100}%) success rate')
|
|
print(f'Waiting {SAMPLE_RATE} seconds until next sample. ')
|
|
time.sleep(SAMPLE_RATE)
|
|
|
|
if i+1 == iterations:
|
|
return # exit
|
|
else:
|
|
i += 1 # continue
|
|
|
|
|
|
def make_alice(known_nodes: Optional[Set[Ursula]] = None):
|
|
"""Initializes a new 'persistent alice configuration' for grant metrics collection."""
|
|
|
|
# This is Alice's PRE payment method.
|
|
pre_payment_method = SubscriptionManagerPayment(
|
|
domain=TACO_DOMAIN, blockchain_endpoint=POLYGON_PROVIDER_URI
|
|
)
|
|
|
|
wallet = Signer.from_signer_uri(f'keystore://{SIGNER_URI}')
|
|
wallet.unlock_account(account=ALICE_ADDRESS, password=SIGNER_PASSWORD)
|
|
|
|
alice_config = AliceConfiguration(
|
|
eth_endpoint=ETHEREUM_PROVIDER_URI,
|
|
polygon_endpoint=POLYGON_PROVIDER_URI,
|
|
checksum_address=ALICE_ADDRESS,
|
|
signer_uri=f'keystore://{SIGNER_URI}',
|
|
config_root=TEMP_ALICE_DIR,
|
|
domain=TACO_DOMAIN,
|
|
known_nodes=known_nodes,
|
|
start_learning_now=False,
|
|
learn_on_same_thread=True,
|
|
gas_strategy=GAS_STRATEGY,
|
|
max_gas_price=MAX_GAS_PRICE,
|
|
)
|
|
|
|
alice_config.initialize(password=INSECURE_PASSWORD)
|
|
alice_config.keystore.unlock(password=INSECURE_PASSWORD)
|
|
alice = alice_config.produce(pre_payment_method=pre_payment_method, signer=wallet)
|
|
alice.start_learning_loop(now=True)
|
|
return alice
|
|
|
|
|
|
def setup():
|
|
"""Prepares the filesystem and logger for grant metrics collection"""
|
|
shutil.rmtree(TEMP_ALICE_DIR, ignore_errors=True)
|
|
GlobalLoggerSettings.start_console_logging()
|
|
GlobalLoggerSettings.start_text_file_logging()
|
|
GlobalLoggerSettings.set_log_level('info')
|
|
|
|
|
|
def aggregate_nodes(eth_endpoint: str) -> Tuple[Set[Ursula], Set[Ursula]]:
|
|
"""generates ursulas from URIs used in grant metrics collection"""
|
|
|
|
seednodes = set()
|
|
if DEFAULT_SEEDNODE_URIS:
|
|
for uri in DEFAULT_SEEDNODE_URIS:
|
|
ursula = Ursula.from_seed_and_stake_info(
|
|
seed_uri=uri, eth_endpoint=eth_endpoint
|
|
)
|
|
seednodes.add(ursula)
|
|
|
|
ursulas = set()
|
|
if HANDPICKED_URSULA_URIS:
|
|
for uri in HANDPICKED_URSULA_URIS:
|
|
ursula = Ursula.from_seed_and_stake_info(
|
|
seed_uri=uri, eth_endpoint=eth_endpoint
|
|
)
|
|
ursulas.add(ursula)
|
|
|
|
return seednodes, ursulas
|
|
|
|
|
|
if __name__ == '__main__':
|
|
setup()
|
|
seednodes, ursulas = aggregate_nodes(eth_endpoint=ETHEREUM_PROVIDER_URI)
|
|
alice = make_alice(known_nodes=seednodes)
|
|
collect(alice=alice, ursulas=ursulas)
|