From e6d1d00f0a757da3cfc5765ceb25a4d6e730893f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=BA=C3=B1ez?= Date: Mon, 19 Oct 2020 17:33:34 +0200 Subject: [PATCH] Fire replacement commitment TXs if we conclude we've waited enough --- nucypher/blockchain/eth/token.py | 35 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/nucypher/blockchain/eth/token.py b/nucypher/blockchain/eth/token.py index cb61fa14b..de574dd2b 100644 --- a/nucypher/blockchain/eth/token.py +++ b/nucypher/blockchain/eth/token.py @@ -29,10 +29,12 @@ from twisted.internet import reactor, task from web3.exceptions import TransactionNotFound from nucypher.blockchain.eth.agents import ContractAgency, StakingEscrowAgent +from nucypher.blockchain.eth.constants import AVERAGE_BLOCK_TIME_IN_SECONDS from nucypher.blockchain.eth.decorators import validate_checksum_address from nucypher.blockchain.eth.registry import BaseContractRegistry from nucypher.blockchain.eth.utils import datetime_at_period from nucypher.types import SubStakeInfo, NuNits, StakerInfo, Period +from nucypher.utilities.gas_strategies import EXPECTED_CONFIRMATION_TIME_IN_SECONDS from nucypher.utilities.logging import Logger @@ -531,6 +533,8 @@ class WorkTracker: INTERVAL_FLOOR = 60 * 15 # fifteen minutes INTERVAL_CEIL = 60 * 180 # three hours + ALLOWED_DEVIATION = 0.5 # i.e., up to +50% from the expected confirmation time + def __init__(self, worker, *args, **kwargs): super().__init__(*args, **kwargs) @@ -538,6 +542,10 @@ class WorkTracker: self.worker = worker self.staking_agent = self.worker.staking_agent + self.gas_strategy = self.staking_agent.blockchain.gas_strategy + expected_time = EXPECTED_CONFIRMATION_TIME_IN_SECONDS[self.gas_strategy] + self.max_confirmation_time = expected_time * (1 + self.ALLOWED_DEVIATION) + self._tracking_task = task.LoopingCall(self._do_work) self._tracking_task.clock = self.CLOCK @@ -654,19 +662,22 @@ class WorkTracker: # self-tracking unmined_transactions = self.__track_pending_commitments(current_block_number=current_block_number) if unmined_transactions: - block_number, txhash = list(self.pending.items())[0] + tx_firing_block_number, txhash = list(self.pending.items())[0] self.log.info(f'Waiting for pending commitment transaction to be mined ({txhash}).') - # TODO: If the transaction is still not mined after threshold number of blocks - # follow-up - possibly issue a replacement transaction? - pending_duration = current_block_number - block_number - if pending_duration > 100: - pass + # If the transaction is still not mined after a max confirmation time (based on current gas strategy), + # issue a replacement transaction. + wait_time_in_blocks = current_block_number - tx_firing_block_number + wait_time_in_seconds = wait_time_in_blocks * AVERAGE_BLOCK_TIME_IN_SECONDS + if wait_time_in_seconds > self.max_confirmation_time: + self.log.info(f"We've waited for {wait_time_in_seconds}, but max time is {self.max_confirmation_time} " + f"for {self.gas_strategy} gas strategy. Issuing a replacement transaction.") + replacement_txhash = self.__fire_commitment() + self.__pending[current_block_number] = replacement_txhash # track this transaction + del self.__pending[tx_firing_block_number] # Assume our original TX is stuck # while there are known pending transactions, remain in fast interval mode self._tracking_task.interval = self.INTERVAL_FLOOR - - # do not commit this iteration return # Randomize the next task interval over time, within bounds. @@ -699,12 +710,16 @@ class WorkTracker: # TODO: Follow-up actions for failed requirement calls return + txhash = self.__fire_commitment(current_block_number) + self.__pending[current_block_number] = txhash # track this transaction + + def __fire_commitment(self): # Make a Commitment - self.log.info("Made a commitment to period {}".format(self.current_period)) transacting_power = self.worker.transacting_power with transacting_power: txhash = self.worker.commit_to_next_period(fire_and_forget=True) # < --- blockchain WRITE - self.__pending[current_block_number] = txhash # track this transaction + self.log.info(f"Making a commitment to period {self.current_period} - TxHash: {txhash}") + return txhash class StakeList(UserList):