Skip to content

Commit

Permalink
fix: revert message retrieval (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
danut13 authored Jun 7, 2024
1 parent cf241bc commit ccdc670
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 29 deletions.
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ repos:
hooks:
- id: mypy
files: ^pantos/common
additional_dependencies: ['types-requests', 'types-PyYAML']
args: ['--namespace-packages', '--explicit-package-bases' ]
additional_dependencies: ['types-requests', 'types-PyYAML', 'web3[tester]']
stages: [ commit ]
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
Expand Down
41 changes: 27 additions & 14 deletions pantos/common/blockchains/ethereum.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
"""The names of methods of the blockchain interactor object which
send transactions."""

_NO_ARCHIVE_NODE_RPC_ERROR_MESSAGE = 'missing trie node'

_NO_ARCHIVE_NODE_LOG_MESSAGE = 'due to the absence of an archive node'

_logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -428,19 +432,28 @@ def __get_node_connections(
def __retrieve_revert_message(
self, transaction_hash: str,
node_connections: NodeConnections[web3.Web3]) -> str:
required_keys = [
'from', 'to', 'gas', 'gasPrice', 'value', 'data', 'nonce',
'maxFeePerGas', 'maxPriorityFeePerGas', 'chainId'
]
full_tx = node_connections.eth.get_transaction(
typing.cast(web3.types.HexStr, transaction_hash)).get()
block_number = full_tx['blockNumber']
replay_tx = {k: v for k, v in full_tx.items() if k in required_keys}
revert_message = ''
revert_message = 'unknown'
try:
node_connections.eth.call(
typing.cast(web3.types.TxParams, replay_tx),
block_number).get()
except web3.exceptions.ContractLogicError as e:
revert_message = str(e)
full_tx = node_connections.eth.get_transaction(
typing.cast(web3.types.HexStr, transaction_hash)).get()
replay_tx = {
'from': full_tx['from'],
'to': full_tx['to'],
'value': full_tx['value'],
'data': full_tx['input']
}
context_block_number = full_tx['blockNumber'] - 1
try:
node_connections.eth.call(
typing.cast(web3.types.TxParams, replay_tx),
context_block_number).get()
except web3.exceptions.ContractLogicError as error:
revert_message = str(error)
except ValueError as error:
if _NO_ARCHIVE_NODE_RPC_ERROR_MESSAGE in error.args[0].get(
'message'):
revert_message += f' {_NO_ARCHIVE_NODE_LOG_MESSAGE}'
except Exception:
_logger.warning('unable to retrieve the revert message',
exc_info=True)
return revert_message
87 changes: 73 additions & 14 deletions tests/blockchains/test_ethereum.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
from pantos.common.blockchains.base import TransactionNonceTooLowError
from pantos.common.blockchains.base import TransactionUnderpricedError
from pantos.common.blockchains.enums import Blockchain
from pantos.common.blockchains.ethereum import _NO_ARCHIVE_NODE_LOG_MESSAGE
from pantos.common.blockchains.ethereum import \
_NO_ARCHIVE_NODE_RPC_ERROR_MESSAGE
from pantos.common.blockchains.ethereum import _TRANSACTION_METHOD_NAMES
from pantos.common.blockchains.ethereum import EthereumUtilities
from pantos.common.blockchains.ethereum import EthereumUtilitiesError
Expand Down Expand Up @@ -511,23 +514,79 @@ def test_create_single_node_connection_not_connected_error(


def test_retrieve_revert_message_correct(ethereum_utilities, w3,
node_connections, transaction_id):
eth_utils = ethereum_utilities
with unittest.mock.patch.object(w3.eth, 'get_transaction',
return_value={'blockNumber': 1}):
node_connections, transaction_id,
transaction_contract_address):
default_account = w3.eth.accounts[0]
with unittest.mock.patch.object(
w3.eth, 'get_transaction', return_value={
'from': default_account,
'to': transaction_contract_address,
'value': 0,
'input': "",
'blockNumber': 1,
}):
with unittest.mock.patch.object(
w3.eth, 'call', side_effect=web3.exceptions.ContractLogicError(
'revert message')):
assert eth_utils._EthereumUtilities__retrieve_revert_message(
transaction_id, node_connections) == 'revert message'
assert \
ethereum_utilities._EthereumUtilities__retrieve_revert_message(
transaction_id, node_connections) == 'revert message'


def test_retrieve_revert_message_no_archive_node_available_error(
ethereum_utilities, w3, node_connections, transaction_id,
transaction_contract_address):
default_account = w3.eth.accounts[0]
with unittest.mock.patch.object(
w3.eth, 'get_transaction', return_value={
'from': default_account,
'to': transaction_contract_address,
'value': 0,
'input': "",
'blockNumber': 1,
}):
with unittest.mock.patch.object(
w3.eth, 'call', side_effect=ValueError({
'message': f'{_NO_ARCHIVE_NODE_RPC_ERROR_MESSAGE} 0x...'
})):
assert \
ethereum_utilities._EthereumUtilities__retrieve_revert_message(
transaction_id, node_connections) == \
f'unknown {_NO_ARCHIVE_NODE_LOG_MESSAGE}'


def test_retrieve_revert_message_correct_no_error(ethereum_utilities, w3,
node_connections,
transaction_id):
eth_utils = ethereum_utilities
with unittest.mock.patch.object(w3.eth, 'get_transaction',
return_value={'blockNumber': 1}):
def test_retrieve_revert_message_correct_no_error(
ethereum_utilities, w3, node_connections, transaction_id,
transaction_contract_address):
default_account = w3.eth.accounts[0]
with unittest.mock.patch.object(
w3.eth, 'get_transaction', return_value={
'from': default_account,
'to': transaction_contract_address,
'value': 0,
'input': "",
'blockNumber': 1,
}):
with unittest.mock.patch.object(w3.eth, 'call', return_value=''):
assert eth_utils._EthereumUtilities__retrieve_revert_message(
transaction_id, node_connections) == ''
assert \
ethereum_utilities._EthereumUtilities__retrieve_revert_message(
transaction_id, node_connections) == 'unknown'


def test_retrieve_revert_message_correct_error(ethereum_utilities, w3,
node_connections,
transaction_id,
transaction_contract_address):
default_account = w3.eth.accounts[0]
with unittest.mock.patch.object(
w3.eth, 'get_transaction', return_value={
'from': default_account,
'to': transaction_contract_address,
'value': 0,
'input': "",
'blockNumber': 1,
}):
with unittest.mock.patch.object(w3.eth, 'call', side_effect=Exception):
assert \
ethereum_utilities._EthereumUtilities__retrieve_revert_message(
transaction_id, node_connections) == 'unknown'

0 comments on commit ccdc670

Please sign in to comment.