Skip to content

Commit

Permalink
Add eth_getRawTransactionByHash (#2051)
Browse files Browse the repository at this point in the history
  • Loading branch information
Moras-del authored Jul 14, 2021
1 parent c28a93b commit 4893947
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 2 deletions.
16 changes: 15 additions & 1 deletion docs/web3.eth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,20 @@ The following methods are available on the ``web3.eth`` namespace.
:attr:`~web3.eth.Eth.get_transaction`


.. py:method:: Eth.get_raw_transaction(transaction_hash)
* Delegates to ``eth_getRawTransactionByHash`` RPC Method

Returns the raw form of transaction specified by ``transaction_hash``.

If no transaction is found, ``TransactionNotFound`` is raised.

.. code-block:: python
>>> web3.eth.get_raw_transaction('0x86fbfe56cce542ff0a2a2716c31675a0c9c43701725c4a751d20ee2ddf8a733d')
HexBytes('0xf86907843b9aca0082520894dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd018086eecac466e115a0f9db4e25484b28f486b247a372708d4cd0643fc63e604133afac577f4cc1eab8a044841d84e799d4dc18ba146816a937e8a0be8bc296bd8bb8aea126de5e627e06')
.. py:method:: Eth.getTransactionFromBlock(block_identifier, transaction_index)
.. note:: This method is deprecated in EIP 1474.
Expand Down Expand Up @@ -748,7 +762,7 @@ The following methods are available on the ``web3.eth`` namespace.
that goes to the miner
* ``gasPrice``: ``integer`` - Integer of the gasPrice used for each paid gas
**LEGACY** - unless you have good reason to, use ``maxFeePerGas``
and ``maxPriorityFeePerGas`` instead.
and ``maxPriorityFeePerGas`` instead.
* ``value``: ``integer`` - (optional) Integer of the value send with this
transaction
* ``data``: ``bytes or text`` - The compiled code of a contract OR the hash
Expand Down
1 change: 1 addition & 0 deletions newsfragments/2039.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for eth_getRawTransactionByHash RPC method
27 changes: 27 additions & 0 deletions tests/core/manager/test_response_formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
from web3._utils.method_formatters import (
raise_block_not_found,
raise_block_not_found_for_uncle_at_index,
raise_transaction_not_found,
)
from web3.exceptions import (
BlockNotFound,
ContractLogicError,
TransactionNotFound,
)

ERROR_RESPONSE = {
Expand All @@ -24,6 +26,7 @@


NONE_RESPONSE = {"jsonrpc": "2.0", "id": 1, "result": None}
ZERO_X_RESPONSE = {"jsonrpc": "2.0", "id": 1, "result": '0x'}


def raise_contract_logic_error(response):
Expand Down Expand Up @@ -89,6 +92,14 @@ def raise_contract_logic_error(response):
raise_block_not_found_for_uncle_at_index,
BlockNotFound,
),
(
# 0x response gets handled the same as a None response
ZERO_X_RESPONSE,
('0x03'),
identity,
raise_transaction_not_found,
TransactionNotFound,
),
],
)
def test_formatted_response_raises_errors(web3,
Expand Down Expand Up @@ -123,6 +134,14 @@ def test_formatted_response_raises_errors(web3,
BlockNotFound,
"Uncle at index: 0 of block with id: '0x01' not found."
),
(
ZERO_X_RESPONSE,
('0x01',),
identity,
raise_transaction_not_found,
TransactionNotFound,
"Transaction with hash: '0x01' not found."
),
],
)
def test_formatted_response_raises_correct_error_message(response,
Expand All @@ -148,6 +167,14 @@ def test_formatted_response_raises_correct_error_message(response,
identity,
NONE_RESPONSE['result'],
),
(
# Response with a result of 0x doesn't raise if there is no null result formatter
ZERO_X_RESPONSE,
('0x03'),
identity,
identity,
ZERO_X_RESPONSE['result'],
),
],
)
def test_formatted_response(response,
Expand Down
4 changes: 4 additions & 0 deletions tests/integration/test_ethereum_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ class TestEthereumTesterEthModule(EthModuleTest):
test_eth_submitWork_deprecated = not_implemented(
EthModuleTest.test_eth_submitWork_deprecated, ValueError)
test_eth_submit_work = not_implemented(EthModuleTest.test_eth_submit_work, ValueError)
test_eth_get_raw_transaction = not_implemented(
EthModuleTest.test_eth_get_raw_transaction, ValueError)
test_eth_get_raw_transaction_raises_error = not_implemented(
EthModuleTest.test_eth_get_raw_transaction, ValueError)

def test_eth_getBlockByHash_pending(
self, web3: "Web3"
Expand Down
2 changes: 2 additions & 0 deletions web3/_utils/method_formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
RPC.eth_getFilterLogs: filter_result_formatter,
RPC.eth_getLogs: filter_result_formatter,
RPC.eth_getProof: apply_formatter_if(is_not_null, proof_formatter),
RPC.eth_getRawTransactionByHash: HexBytes,
RPC.eth_getStorageAt: HexBytes,
RPC.eth_getTransactionByBlockHashAndIndex: apply_formatter_if(
is_not_null,
Expand Down Expand Up @@ -657,6 +658,7 @@ def raise_transaction_not_found_with_index(params: Tuple[BlockIdentifier, int])
RPC.eth_getTransactionByBlockHashAndIndex: raise_transaction_not_found_with_index,
RPC.eth_getTransactionByBlockNumberAndIndex: raise_transaction_not_found_with_index,
RPC.eth_getTransactionReceipt: raise_transaction_not_found,
RPC.eth_getRawTransactionByHash: raise_transaction_not_found,
}


Expand Down
26 changes: 26 additions & 0 deletions web3/_utils/module_testing/eth_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,20 @@ async def test_eth_getBlockByNumber_full_transactions(
transaction = block['transactions'][0]
assert transaction['hash'] == block_with_txn['transactions'][0]

@pytest.mark.asyncio
async def test_eth_get_raw_transaction(
self, async_w3: "Web3", mined_txn_hash: HexStr
) -> None:
raw_transaction = await async_w3.eth.get_raw_transaction(mined_txn_hash) # type: ignore
assert is_bytes(raw_transaction)

@pytest.mark.asyncio
async def test_eth_get_raw_transaction_raises_error(
self, web3: "Web3", mined_txn_hash: HexStr
) -> None:
with pytest.raises(TransactionNotFound, match=f"Transaction with hash: '{UNKNOWN_HASH}'"):
web3.eth.get_raw_transaction(UNKNOWN_HASH)


class EthModuleTest:
def test_eth_protocol_version(self, web3: "Web3") -> None:
Expand Down Expand Up @@ -2168,3 +2182,15 @@ def test_eth_submitWork_deprecated(self, web3: "Web3") -> None:
match="submitWork is deprecated in favor of submit_work"):
result = web3.eth.submitWork(nonce, pow_hash, mix_digest)
assert result is False

def test_eth_get_raw_transaction(
self, web3: "Web3", mined_txn_hash: HexStr
) -> None:
raw_transaction = web3.eth.get_raw_transaction(mined_txn_hash)
assert is_bytes(raw_transaction)

def test_eth_get_raw_transaction_raises_error(
self, web3: "Web3", mined_txn_hash: HexStr
) -> None:
with pytest.raises(TransactionNotFound, match=f"Transaction with hash: '{UNKNOWN_HASH}'"):
web3.eth.get_raw_transaction(UNKNOWN_HASH)
2 changes: 2 additions & 0 deletions web3/_utils/rpc_abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class RPC:
eth_getFilterLogs = RPCEndpoint("eth_getFilterLogs")
eth_getLogs = RPCEndpoint("eth_getLogs")
eth_getProof = RPCEndpoint("eth_getProof")
eth_getRawTransactionByHash = RPCEndpoint("eth_getRawTransactionByHash")
eth_getStorageAt = RPCEndpoint("eth_getStorageAt")
eth_getTransactionByBlockHashAndIndex = RPCEndpoint("eth_getTransactionByBlockHashAndIndex")
eth_getTransactionByBlockNumberAndIndex = RPCEndpoint("eth_getTransactionByBlockNumberAndIndex")
Expand Down Expand Up @@ -175,6 +176,7 @@ class RPC:
'eth_getBlockTransactionCountByHash': ['bytes32'],
'eth_getCode': ['address', None],
'eth_getLogs': FILTER_PARAMS_ABIS,
'eth_getRawTransactionByHash': ['bytes32'],
'eth_getStorageAt': ['address', 'uint', None],
'eth_getProof': ['address', 'uint[]', None],
'eth_getTransactionByBlockHashAndIndex': ['bytes32', 'uint'],
Expand Down
12 changes: 12 additions & 0 deletions web3/eth.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ def send_transaction_munger(self, transaction: TxParams) -> Tuple[TxParams]:
mungers=[default_root_munger]
)

_get_raw_transaction: Method[Callable[[_Hash32], HexBytes]] = Method(
RPC.eth_getRawTransactionByHash,
mungers=[default_root_munger]
)

def _generate_gas_price(self, transaction_params: Optional[TxParams] = None) -> Optional[Wei]:
if self.gasPriceStrategy:
return self.gasPriceStrategy(self.web3, transaction_params)
Expand Down Expand Up @@ -205,6 +210,10 @@ async def get_transaction(self, transaction_hash: _Hash32) -> TxData:
# types ignored b/c mypy conflict with BlockingEth properties
return await self._get_transaction(transaction_hash) # type: ignore

async def get_raw_transaction(self, transaction_hash: _Hash32) -> TxData:
# types ignored b/c mypy conflict with BlockingEth properties
return await self._get_raw_transaction(transaction_hash) # type: ignore

async def generate_gas_price(
self, transaction_params: Optional[TxParams] = None
) -> Optional[Wei]:
Expand Down Expand Up @@ -501,6 +510,9 @@ def get_block(
def get_transaction(self, transaction_hash: _Hash32) -> TxData:
return self._get_transaction(transaction_hash)

def get_raw_transaction(self, transaction_hash: _Hash32) -> _Hash32:
return self._get_raw_transaction(transaction_hash)

def getTransactionFromBlock(
self, block_identifier: BlockIdentifier, transaction_index: int
) -> NoReturn:
Expand Down
8 changes: 7 additions & 1 deletion web3/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
from eth_utils.toolz import (
pipe,
)
from hexbytes import (
HexBytes,
)

from web3._utils.decorators import (
deprecated_for,
Expand Down Expand Up @@ -53,6 +56,9 @@
from web3 import Web3 # noqa: F401


NULL_RESPONSES = [None, HexBytes('0x'), '0x']


def apply_error_formatters(
error_formatters: Callable[..., Any],
response: RPCResponse,
Expand Down Expand Up @@ -160,7 +166,7 @@ def formatted_response(
if "error" in response:
apply_error_formatters(error_formatters, response)
raise ValueError(response["error"])
elif response['result'] is None:
elif response['result'] in NULL_RESPONSES:
# null_result_formatters raise either a BlockNotFound
# or a TransactionNotFound error, depending on the method called
apply_null_result_formatters(null_result_formatters, response, params)
Expand Down
1 change: 1 addition & 0 deletions web3/middleware/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
'eth_getTransactionByBlockHashAndIndex',
# 'eth_getTransactionByBlockNumberAndIndex',
# 'eth_getTransactionReceipt',
'eth_getRawTransactionByHash',
'eth_getUncleByBlockHashAndIndex',
# 'eth_getUncleByBlockNumberAndIndex',
# 'eth_getCompilers',
Expand Down
1 change: 1 addition & 0 deletions web3/middleware/exception_retry_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
'eth_getTransactionByBlockNumberAndIndex',
'eth_getTransactionReceipt',
'eth_getTransactionCount',
'eth_getRawTransactionByHash',
'eth_call',
'eth_estimateGas',
'eth_newBlockFilter',
Expand Down

0 comments on commit 4893947

Please sign in to comment.