Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/rebroadcast #1327

Merged
merged 7 commits into from
Nov 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 38 additions & 20 deletions brownie/network/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import sys
import threading
import time
from collections import deque
from collections.abc import Iterator
from getpass import getpass
from pathlib import Path
Expand All @@ -20,7 +21,7 @@
from eth_utils.applicators import apply_formatters_to_dict
from hexbytes import HexBytes
from web3 import HTTPProvider, IPCProvider
from web3.exceptions import InvalidTransaction
from web3.exceptions import InvalidTransaction, TransactionNotFound

from brownie._config import CONFIG, _get_data_folder
from brownie._singleton import _Singleton
Expand All @@ -43,6 +44,7 @@
rpc = Rpc()

eth_account.Account.enable_unaudited_hdwallet_features()
_marker = deque("-/|\\-/|\\")


class Accounts(metaclass=_Singleton):
Expand Down Expand Up @@ -736,25 +738,41 @@ def _make_transaction(
if to:
tx["to"] = to_address(str(to))
tx = _apply_fee_to_tx(tx, gas_price, max_fee, priority_fee)
try:
txid = self._transact(tx, allow_revert) # type: ignore
exc, revert_data = None, None
except ValueError as e:
exc = VirtualMachineError(e)
if not hasattr(exc, "txid"):
raise exc from None
txid = exc.txid
revert_data = (exc.revert_msg, exc.pc, exc.revert_type)

receipt = TransactionReceipt(
txid,
self,
silent=silent,
required_confs=required_confs,
is_blocking=False,
name=fn_name,
revert_data=revert_data,
)
txid = None
while True:
try:
response = self._transact(tx, allow_revert) # type: ignore
exc, revert_data = None, None
if txid is None:
txid = HexBytes(response).hex()
if not silent:
print(f"\rTransaction sent: {color('bright blue')}{txid}{color}")
except ValueError as e:
if txid is None:
exc = VirtualMachineError(e)
if not hasattr(exc, "txid"):
raise exc from None
txid = exc.txid
print(f"\rTransaction sent: {color('bright blue')}{txid}{color}")
revert_data = (exc.revert_msg, exc.pc, exc.revert_type)
try:
receipt = TransactionReceipt(
txid,
self,
silent=silent,
required_confs=required_confs,
is_blocking=False,
name=fn_name,
revert_data=revert_data,
) # type: ignore
break
except (TransactionNotFound, ValueError):
if not silent:
sys.stdout.write(f" Awaiting transaction in the mempool... {_marker[0]}\r")
sys.stdout.flush()
_marker.rotate(1)
time.sleep(1)

receipt = self._await_confirmation(receipt, required_confs, gas_strategy, gas_iter)
return receipt, exc

Expand Down
5 changes: 4 additions & 1 deletion brownie/network/middlewares/caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,14 @@ def process_request(self, make_request: Callable, method: str, params: List) ->
"eth_uninstallFilter",
# used to check connectivity
"web3_clientVersion",
# caching these causes weirdness with transaction replacement
# caching these causes weirdness with transaction broadcasting and replacement
"eth_sendTransaction",
"eth_sendRawTransaction",
"eth_sign",
"eth_signTransaction",
"eth_getTransactionByHash",
"eth_getTransactionReceipt",
"eth_chainId",
):
return make_request(method, params)

Expand Down
84 changes: 30 additions & 54 deletions brownie/network/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,6 @@ def __init__(

if isinstance(txid, bytes):
txid = HexBytes(txid).hex()
if not self._silent:
print(f"\rTransaction sent: {color('bright blue')}{txid}{color}")

# this event is set once the transaction is confirmed or dropped
# it is used to waiting during blocking transaction actions
Expand Down Expand Up @@ -197,7 +195,34 @@ def __init__(
if self._revert_pc is not None:
self._dev_revert_msg = build._get_dev_revert(self._revert_pc) or None

self._await_transaction(required_confs, is_blocking)
tx: Dict = web3.eth.get_transaction(HexBytes(self.txid))
self._set_from_tx(tx)

if not self._silent:
output_str = ""
if self.type == 2:
max_gas = tx["maxFeePerGas"] / 10 ** 9
priority_gas = tx["maxPriorityFeePerGas"] / 10 ** 9
output_str = (
f" Max fee: {color('bright blue')}{max_gas}{color} gwei"
f" Priority fee: {color('bright blue')}{priority_gas}{color} gwei"
)
elif self.gas_price is not None:
gas_price = self.gas_price / 10 ** 9
output_str = f" Gas price: {color('bright blue')}{gas_price}{color} gwei"
print(
f"{output_str} Gas limit: {color('bright blue')}{self.gas_limit}{color}"
f" Nonce: {color('bright blue')}{self.nonce}{color}"
)

# await confirmation of tx in a separate thread which is blocking if
# required_confs > 0 or tx has already confirmed (`blockNumber` != None)
confirm_thread = threading.Thread(
target=self._await_confirmation, args=(tx["blockNumber"], required_confs), daemon=True
)
confirm_thread.start()
if is_blocking and (required_confs > 0 or tx["blockNumber"]):
confirm_thread.join()

def __repr__(self) -> str:
color_str = {-2: "dark white", -1: "bright yellow", 0: "bright red", 1: ""}[self.status]
Expand Down Expand Up @@ -422,56 +447,6 @@ def _raise_if_reverted(self, exc: Any) -> None:
source=source, revert_msg=self._revert_msg, dev_revert_msg=self._dev_revert_msg
)

def _await_transaction(self, required_confs: int, is_blocking: bool) -> None:
# await tx showing in mempool
while True:
try:
tx: Dict = web3.eth.get_transaction(HexBytes(self.txid))
break
except (TransactionNotFound, ValueError):
if self.sender is None:
# if sender was not explicitly set, this transaction was
# not broadcasted locally and so likely doesn't exist
raise
if self.nonce is not None:
sender_nonce = web3.eth.get_transaction_count(str(self.sender))
if sender_nonce > self.nonce:
self.status = Status(-2)
return
if not self._silent:
sys.stdout.write(f" Awaiting transaction in the mempool... {_marker[0]}\r")
sys.stdout.flush()
_marker.rotate(1)
time.sleep(1)

self._set_from_tx(tx)

if not self._silent:
output_str = ""
if self.type == 2:
max_gas = tx["maxFeePerGas"] / 10 ** 9
priority_gas = tx["maxPriorityFeePerGas"] / 10 ** 9
output_str = (
f" Max fee: {color('bright blue')}{max_gas}{color} gwei"
f" Priority fee: {color('bright blue')}{priority_gas}{color} gwei"
)
elif self.gas_price is not None:
gas_price = self.gas_price / 10 ** 9
output_str = f" Gas price: {color('bright blue')}{gas_price}{color} gwei"
print(
f"{output_str} Gas limit: {color('bright blue')}{self.gas_limit}{color}"
f" Nonce: {color('bright blue')}{self.nonce}{color}"
)

# await confirmation of tx in a separate thread which is blocking if
# required_confs > 0 or tx has already confirmed (`blockNumber` != None)
confirm_thread = threading.Thread(
target=self._await_confirmation, args=(tx["blockNumber"], required_confs), daemon=True
)
confirm_thread.start()
if is_blocking and (required_confs > 0 or tx["blockNumber"]):
confirm_thread.join()

def _await_confirmation(self, block_number: int = None, required_confs: int = 1) -> None:
# await first confirmation
block_number = block_number or self.block_number
Expand Down Expand Up @@ -512,7 +487,8 @@ def _await_confirmation(self, block_number: int = None, required_confs: int = 1)
)
_marker.rotate(1)
sys.stdout.flush()
time.sleep(1)

time.sleep(1)

# silence other dropped tx's immediately after confirmation to avoid output weirdness
for dropped_tx in state.TxHistory().filter(
Expand Down
38 changes: 0 additions & 38 deletions tests/network/transaction/test_confirmation.py

This file was deleted.