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

Add support for eth_getRawTransactionByHash #2051

Merged
merged 1 commit into from
Jul 14, 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
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