mirror of https://github.com/nucypher/nucypher.git
repsond to RFCs in PR #3437:
- removes stale newsfrag - do not return Deferred from SimpleTask.start and SimpleTask.stop - removes eager atxm startup with ursula startup. - remove unused token agent methods - add spaces after periods in select logs - more...pull/3475/head
parent
b1fef5d6b1
commit
edbead6791
|
@ -1 +0,0 @@
|
|||
Introduces automated protocol transaction retries
|
|
@ -1,6 +1,7 @@
|
|||
import json
|
||||
import random
|
||||
import time
|
||||
import traceback
|
||||
from collections import defaultdict
|
||||
from decimal import Decimal
|
||||
from typing import DefaultDict, Dict, List, Optional, Set, Union
|
||||
|
@ -25,7 +26,6 @@ from nucypher_core.ferveo import (
|
|||
Transcript,
|
||||
Validator,
|
||||
)
|
||||
from twisted.internet import reactor
|
||||
from web3 import HTTPProvider, Web3
|
||||
from web3.types import TxReceipt
|
||||
|
||||
|
@ -436,23 +436,13 @@ class Operator(BaseActor):
|
|||
to be handled by the EventActuator.
|
||||
"""
|
||||
if self.checksum_address not in participants:
|
||||
# ERROR: This is an abnormal state since this method
|
||||
# is designed to be invoked only when this node
|
||||
# is an on-chain participant in the Coordinator.StartRitual event.
|
||||
#
|
||||
# This is a *nearly* a critical error. It's possible that the
|
||||
# log it as an error and return None to avoid crashing upstack
|
||||
# async tasks and drawing unnecessary amounts of attention to the issue.
|
||||
message = (
|
||||
f"{self.checksum_address}|{self.wallet_address} "
|
||||
f"is not a member of ritual {ritual_id}"
|
||||
)
|
||||
try:
|
||||
raise RuntimeError(message)
|
||||
finally:
|
||||
# log and stop reactor; this will crash the application.
|
||||
self.log.critical(message)
|
||||
return reactor.stop()
|
||||
stack_trace = traceback.format_stack()
|
||||
self.log.critical(f"{message}\n{stack_trace}")
|
||||
return
|
||||
|
||||
# check phase 1 contract state
|
||||
if not self._is_phase_1_action_required(ritual_id=ritual_id):
|
||||
|
|
|
@ -141,69 +141,6 @@ class NucypherTokenAgent(EthereumContractAgent):
|
|||
balance: int = self.contract.functions.balanceOf(address).call()
|
||||
return types.NuNits(balance)
|
||||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
def get_allowance(
|
||||
self, owner: ChecksumAddress, spender: ChecksumAddress
|
||||
) -> types.NuNits:
|
||||
"""Check the amount of tokens that an owner allowed to a spender"""
|
||||
allowance: int = self.contract.functions.allowance(owner, spender).call()
|
||||
return types.NuNits(allowance)
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def increase_allowance(
|
||||
self,
|
||||
transacting_power: TransactingPower,
|
||||
spender_address: ChecksumAddress,
|
||||
increase: types.NuNits,
|
||||
) -> TxReceipt:
|
||||
"""Increase the allowance of a spender address funded by a sender address"""
|
||||
contract_function: ContractFunction = self.contract.functions.increaseAllowance(
|
||||
spender_address, increase
|
||||
)
|
||||
receipt: TxReceipt = self.blockchain.send_transaction(
|
||||
contract_function=contract_function, transacting_power=transacting_power
|
||||
)
|
||||
return receipt
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def decrease_allowance(
|
||||
self,
|
||||
transacting_power: TransactingPower,
|
||||
spender_address: ChecksumAddress,
|
||||
decrease: types.NuNits,
|
||||
) -> TxReceipt:
|
||||
"""Decrease the allowance of a spender address funded by a sender address"""
|
||||
contract_function: ContractFunction = self.contract.functions.decreaseAllowance(
|
||||
spender_address, decrease
|
||||
)
|
||||
receipt: TxReceipt = self.blockchain.send_transaction(
|
||||
contract_function=contract_function, transacting_power=transacting_power
|
||||
)
|
||||
return receipt
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def approve_transfer(
|
||||
self,
|
||||
amount: types.NuNits,
|
||||
spender_address: ChecksumAddress,
|
||||
transacting_power: TransactingPower,
|
||||
) -> TxReceipt:
|
||||
"""Approve the spender address to transfer an amount of tokens on behalf of the sender address"""
|
||||
self._validate_zero_allowance(amount, spender_address, transacting_power)
|
||||
|
||||
payload: TxParams = {
|
||||
"gas": Wei(500_000)
|
||||
} # TODO #842: gas needed for use with geth! <<<< Is this still open?
|
||||
contract_function: ContractFunction = self.contract.functions.approve(
|
||||
spender_address, amount
|
||||
)
|
||||
receipt: TxReceipt = self.blockchain.send_transaction(
|
||||
contract_function=contract_function,
|
||||
payload=payload,
|
||||
transacting_power=transacting_power,
|
||||
)
|
||||
return receipt
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def transfer(
|
||||
self,
|
||||
|
@ -220,41 +157,6 @@ class NucypherTokenAgent(EthereumContractAgent):
|
|||
)
|
||||
return receipt
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def approve_and_call(
|
||||
self,
|
||||
amount: types.NuNits,
|
||||
target_address: ChecksumAddress,
|
||||
transacting_power: TransactingPower,
|
||||
call_data: bytes = b"",
|
||||
gas_limit: Optional[Wei] = None,
|
||||
) -> TxReceipt:
|
||||
self._validate_zero_allowance(amount, target_address, transacting_power)
|
||||
|
||||
payload = None
|
||||
if gas_limit: # TODO: Gas management - #842
|
||||
payload = {"gas": gas_limit}
|
||||
approve_and_call: ContractFunction = self.contract.functions.approveAndCall(
|
||||
target_address, amount, call_data
|
||||
)
|
||||
approve_and_call_receipt: TxReceipt = self.blockchain.send_transaction(
|
||||
contract_function=approve_and_call,
|
||||
transacting_power=transacting_power,
|
||||
payload=payload,
|
||||
)
|
||||
return approve_and_call_receipt
|
||||
|
||||
def _validate_zero_allowance(self, amount, target_address, transacting_power):
|
||||
if amount == 0:
|
||||
return
|
||||
current_allowance = self.get_allowance(
|
||||
owner=transacting_power.account, spender=target_address
|
||||
)
|
||||
if current_allowance != 0:
|
||||
raise self.RequirementError(
|
||||
f"Token allowance for spender {target_address} must be 0"
|
||||
)
|
||||
|
||||
|
||||
class SubscriptionManagerAgent(EthereumContractAgent):
|
||||
contract_name: str = SUBSCRIPTION_MANAGER_CONTRACT_NAME
|
||||
|
@ -829,12 +731,12 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
contract_function: ContractFunction = self.contract.functions.postTranscript(
|
||||
ritualId=ritual_id, transcript=bytes(transcript)
|
||||
)
|
||||
atx = self.blockchain.send_async_transaction(
|
||||
async_tx = self.blockchain.send_async_transaction(
|
||||
contract_function=contract_function,
|
||||
transacting_power=transacting_power,
|
||||
info={"ritual_id": ritual_id, "phase": PHASE1},
|
||||
)
|
||||
return atx
|
||||
return async_tx
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def post_aggregation(
|
||||
|
@ -851,13 +753,13 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
dkgPublicKey=Ferveo.G1Point.from_dkg_public_key(public_key),
|
||||
decryptionRequestStaticKey=bytes(participant_public_key),
|
||||
)
|
||||
atx = self.blockchain.send_async_transaction(
|
||||
async_tx = self.blockchain.send_async_transaction(
|
||||
contract_function=contract_function,
|
||||
gas_estimation_multiplier=1.4,
|
||||
transacting_power=transacting_power,
|
||||
info={"ritual_id": ritual_id, "phase": PHASE2},
|
||||
)
|
||||
return atx
|
||||
return async_tx
|
||||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
def get_ritual_initiation_cost(
|
||||
|
|
|
@ -133,8 +133,8 @@ class BlockchainInterface:
|
|||
else:
|
||||
cost = transaction_fee + self.payload.get("value", 0)
|
||||
message = (
|
||||
f'{self.name} from {self.payload["from"][:8]} - {self.base_message}.'
|
||||
f"Calculated cost is {prettify_eth_amount(cost)},"
|
||||
f'{self.name} from {self.payload["from"][:8]} - {self.base_message}. '
|
||||
f"Calculated cost is {prettify_eth_amount(cost)}, "
|
||||
f"but sender only has {prettify_eth_amount(self.get_balance())}."
|
||||
)
|
||||
return message
|
||||
|
@ -656,7 +656,7 @@ class BlockchainInterface:
|
|||
tx = self.client.get_transaction(txhash)
|
||||
if tx["gas"] == receipt["gasUsed"]:
|
||||
raise self.InterfaceError(
|
||||
f"Transaction consumed 100% of transaction gas."
|
||||
f"Transaction consumed 100% of transaction gas. "
|
||||
f"Full receipt: \n {pprint.pformat(receipt, indent=2)}"
|
||||
)
|
||||
|
||||
|
|
|
@ -55,10 +55,10 @@ class EventScannerTask(SimpleTask):
|
|||
self.scanner = scanner
|
||||
super().__init__()
|
||||
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
self.scanner()
|
||||
|
||||
def handle_errors(self, *args, **kwargs):
|
||||
def handle_errors(self, *args, **kwargs) -> None:
|
||||
self.log.warn(
|
||||
"Error during ritual event scanning: {}".format(args[0].getTraceback())
|
||||
)
|
||||
|
@ -209,13 +209,13 @@ class ActiveRitualTracker:
|
|||
# if non-zero block found - return the block before
|
||||
return expected_start_block.number - 1 if expected_start_block.number > 0 else 0
|
||||
|
||||
def start(self):
|
||||
def start(self) -> None:
|
||||
"""Start the event scanner task."""
|
||||
return self.task.start()
|
||||
self.task.start()
|
||||
|
||||
def stop(self):
|
||||
def stop(self) -> None:
|
||||
"""Stop the event scanner task."""
|
||||
return self.task.stop()
|
||||
self.task.stop()
|
||||
|
||||
def _action_required(self, ritual_event: AttributeDict) -> bool:
|
||||
"""Check if an action is required for a given ritual event."""
|
||||
|
|
|
@ -9,11 +9,11 @@ class PrometheusMetricsTracker(SimpleTask):
|
|||
self.metrics_collectors = collectors
|
||||
super().__init__(interval=interval)
|
||||
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
for collector in self.metrics_collectors:
|
||||
collector.collect()
|
||||
|
||||
def handle_errors(self, *args, **kwargs):
|
||||
def handle_errors(self, *args, **kwargs) -> None:
|
||||
self.log.warn(
|
||||
"Error during prometheus metrics collection: {}".format(
|
||||
args[0].getTraceback()
|
||||
|
|
|
@ -950,12 +950,11 @@ class Ursula(Teacher, Character, Operator):
|
|||
preflight: bool = True,
|
||||
block_until_ready: bool = True,
|
||||
eager: bool = False,
|
||||
transaction_tracking: bool = True,
|
||||
) -> None:
|
||||
"""Schedule and start select ursula services, then optionally start the reactor."""
|
||||
|
||||
BlockchainInterfaceFactory.get_or_create_interface(endpoint=self.eth_endpoint)
|
||||
polygon = BlockchainInterfaceFactory.get_or_create_interface(
|
||||
BlockchainInterfaceFactory.get_or_create_interface(
|
||||
endpoint=self.polygon_endpoint
|
||||
)
|
||||
|
||||
|
@ -974,13 +973,6 @@ class Ursula(Teacher, Character, Operator):
|
|||
if emitter:
|
||||
emitter.message(f"✓ P2P Networking ({self.domain})", color="green")
|
||||
|
||||
if transaction_tracking:
|
||||
# Uncomment to enable tracking for both chains.
|
||||
# mainnet.tracker.start(now=False)
|
||||
polygon.tx_machine.start(now=False)
|
||||
if emitter:
|
||||
emitter.message("✓ Transaction Autopilot", color="green")
|
||||
|
||||
if ritual_tracking:
|
||||
self.ritual_tracker.start()
|
||||
if emitter:
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet.defer import Deferred
|
||||
from twisted.internet.task import LoopingCall
|
||||
from twisted.python.failure import Failure
|
||||
|
||||
|
@ -25,25 +24,24 @@ class SimpleTask(ABC):
|
|||
"""Determine whether the task is already running."""
|
||||
return self._task.running
|
||||
|
||||
def start(self, now: bool = False) -> Deferred:
|
||||
def start(self, now: bool = False) -> None:
|
||||
"""Start task."""
|
||||
if not self.running:
|
||||
d = self._task.start(interval=self.interval, now=now)
|
||||
d.addErrback(self.handle_errors)
|
||||
return d
|
||||
|
||||
def stop(self):
|
||||
def stop(self) -> None:
|
||||
"""Stop task."""
|
||||
if self.running:
|
||||
self._task.stop()
|
||||
|
||||
@abstractmethod
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
"""Task method that should be periodically run."""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def handle_errors(self, *args, **kwargs):
|
||||
def handle_errors(self, *args, **kwargs) -> None:
|
||||
"""Error callback for error handling during execution."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
|
@ -129,9 +129,9 @@ def test_post_transcript(agent, transcripts, transacting_powers, testerchain, cl
|
|||
yield clock.advance(testerchain.tx_machine._task.interval)
|
||||
testerchain.tx_machine.stop()
|
||||
|
||||
for i, atx in enumerate(txs):
|
||||
for i, async_tx in enumerate(txs):
|
||||
post_transcript_events = (
|
||||
agent.contract.events.TranscriptPosted().process_receipt(atx.receipt)
|
||||
agent.contract.events.TranscriptPosted().process_receipt(async_tx.receipt)
|
||||
)
|
||||
# assert len(post_transcript_events) == 1
|
||||
event = post_transcript_events[0]
|
||||
|
@ -181,10 +181,10 @@ def test_post_aggregation(
|
|||
yield clock.advance(testerchain.tx_machine._task.interval)
|
||||
testerchain.tx_machine.stop()
|
||||
|
||||
for i, atx in enumerate(txs):
|
||||
for i, async_tx in enumerate(txs):
|
||||
participant_public_keys[cohort[i]] = participant_public_key
|
||||
post_aggregation_events = (
|
||||
agent.contract.events.AggregationPosted().process_receipt(atx.receipt)
|
||||
agent.contract.events.AggregationPosted().process_receipt(async_tx.receipt)
|
||||
)
|
||||
assert len(post_aggregation_events) == 1
|
||||
event = post_aggregation_events[0]
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
|
||||
|
||||
|
||||
import pytest
|
||||
from eth_tester.exceptions import TransactionFailed
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, NucypherTokenAgent
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from tests.constants import TEST_ETH_PROVIDER_URI
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def agent(testerchain, test_registry) -> NucypherTokenAgent:
|
||||
token_agent = ContractAgency.get_agent(
|
||||
NucypherTokenAgent,
|
||||
registry=test_registry,
|
||||
blockchain_endpoint=TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
return token_agent
|
||||
|
||||
|
||||
@pytest.mark.skip()
|
||||
def test_token_properties(agent):
|
||||
testerchain = agent.blockchain
|
||||
|
||||
# Internal
|
||||
assert 'NuCypher' == agent.contract.functions.name().call()
|
||||
assert 18 == agent.contract.functions.decimals().call()
|
||||
assert 'NU' == agent.contract.functions.symbol().call()
|
||||
|
||||
# Cannot transfer any ETH to token contract
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
origin = testerchain.client.coinbase
|
||||
payload = {'from': origin, 'to': agent.contract_address, 'value': 1}
|
||||
tx = testerchain.client.send_transaction(payload)
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
assert len(agent.contract_address) == 42
|
||||
assert agent.contract.address == agent.contract_address
|
||||
assert agent.contract_name == NucypherTokenAgent.contract_name
|
||||
|
||||
|
||||
@pytest.mark.skip()
|
||||
def test_get_balance(agent):
|
||||
testerchain = agent.blockchain
|
||||
deployer, someone, *everybody_else = testerchain.client.accounts
|
||||
balance = agent.get_balance(address=someone)
|
||||
assert balance == 0
|
||||
balance = agent.get_balance(address=deployer)
|
||||
assert balance > 0
|
||||
|
||||
|
||||
@pytest.mark.skip()
|
||||
def test_approve_transfer(agent, taco_application_agent):
|
||||
testerchain = agent.blockchain
|
||||
deployer, someone, *everybody_else = testerchain.client.accounts
|
||||
tpower = TransactingPower(account=someone, signer=Web3Signer(testerchain.client))
|
||||
|
||||
# Approve
|
||||
receipt = agent.approve_transfer(
|
||||
amount=taco_application_agent.get_min_authorization(),
|
||||
spender_address=agent.contract_address,
|
||||
transacting_power=tpower,
|
||||
)
|
||||
|
||||
assert receipt['status'] == 1, "Transaction Rejected"
|
||||
assert receipt['logs'][0]['address'] == agent.contract_address
|
||||
|
||||
|
||||
@pytest.mark.skip()
|
||||
def test_transfer(agent, taco_application_agent):
|
||||
testerchain = agent.blockchain
|
||||
origin, someone, *everybody_else = testerchain.client.accounts
|
||||
tpower = TransactingPower(account=origin, signer=Web3Signer(testerchain.client))
|
||||
|
||||
old_balance = agent.get_balance(someone)
|
||||
receipt = agent.transfer(
|
||||
amount=taco_application_agent.get_min_authorization(),
|
||||
target_address=someone,
|
||||
transacting_power=tpower,
|
||||
)
|
||||
|
||||
assert receipt['status'] == 1, "Transaction Rejected"
|
||||
assert receipt['logs'][0]['address'] == agent.contract_address
|
||||
|
||||
new_balance = agent.get_balance(someone)
|
||||
assert new_balance == old_balance + taco_application_agent.get_min_authorization()
|
||||
|
||||
|
||||
@pytest.mark.skip()
|
||||
def test_approve_and_call(agent, taco_application_agent, deploy_contract):
|
||||
testerchain = agent.blockchain
|
||||
deployer, someone, *everybody_else = testerchain.client.accounts
|
||||
|
||||
mock_target, _ = deploy_contract('ReceiveApprovalMethodMock')
|
||||
|
||||
# Approve and call
|
||||
tpower = TransactingPower(account=someone, signer=Web3Signer(testerchain.client))
|
||||
call_data = b"Good morning, that's a nice tnetennba."
|
||||
receipt = agent.approve_and_call(
|
||||
amount=taco_application_agent.get_min_authorization(),
|
||||
target_address=mock_target.address,
|
||||
transacting_power=tpower,
|
||||
call_data=call_data,
|
||||
)
|
||||
|
||||
assert receipt['status'] == 1, "Transaction Rejected"
|
||||
assert receipt['logs'][0]['address'] == agent.contract_address
|
||||
|
||||
assert mock_target.functions.extraData().call() == call_data
|
||||
assert mock_target.functions.sender().call() == someone
|
||||
assert (
|
||||
mock_target.functions.value().call()
|
||||
== taco_application_agent.get_min_authorization()
|
||||
)
|
|
@ -20,6 +20,7 @@ def test_operator_never_bonded(mocker, get_random_checksum_address):
|
|||
|
||||
tracker = OperatorBondedTracker(ursula=ursula)
|
||||
try:
|
||||
threads.deferToThread(tracker.start)
|
||||
with pytest.raises(OperatorBondedTracker.OperatorNoLongerBonded):
|
||||
d = threads.deferToThread(tracker.run)
|
||||
yield d
|
||||
|
@ -43,6 +44,9 @@ def test_operator_bonded_but_becomes_unbonded(mocker, get_random_checksum_addres
|
|||
|
||||
tracker = OperatorBondedTracker(ursula=ursula)
|
||||
try:
|
||||
threads.deferToThread(tracker.start)
|
||||
|
||||
# bonded
|
||||
for i in range(1, 10):
|
||||
d = threads.deferToThread(tracker.run)
|
||||
yield d
|
||||
|
|
Loading…
Reference in New Issue