Skip to content

Commit 2995523

Browse files
committed
Add tests for CCIP Read offchain resolution
1 parent 4bbd95f commit 2995523

File tree

5 files changed

+88
-4
lines changed

5 files changed

+88
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// This contract is meant to test CCIP Read / Offchain Lookup functionality as part of EIP-3668. Multiple functions
2+
// may be added here for testing and the contract can be recompiled for `test_offchain_lookup.py` and other tests.
3+
4+
pragma solidity ^0.8.13;
5+
6+
contract OffchainLookup {
7+
string[] urls = ["https://web3.py/gateway/{sender}/{data}"];
8+
9+
error OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData);
10+
11+
// This function is meant to test that continuous OffchainLookup reverts raise an exception after too many
12+
// redirects. This example technically breaks the flow described in EIP-3668 but this is solely meant to trigger
13+
// continuous OffchainLookup reverts and test that we catch this sort of activity and stop it. Currently this limit
14+
// is set to 4 redirects.
15+
function continuousOffchainLookup() external returns(bytes memory) {
16+
bytes memory callData;
17+
18+
revert OffchainLookup(
19+
address(this),
20+
urls,
21+
callData,
22+
this.continuousOffchainLookup.selector,
23+
callData
24+
);
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import json
2+
import pytest
3+
4+
import requests
5+
6+
from web3.exceptions import (
7+
TooManyRequests,
8+
)
9+
10+
11+
class MockHttpSuccessResponse:
12+
def __init__(self, *args, **_kwargs):
13+
pass
14+
# assert args[1] == "https://web3.py/gateway/0xf2e246bb76df876cef8b38ae84130f4f55de395b/0x09a3c01b" # noqa: E501
15+
16+
@staticmethod
17+
def raise_for_status():
18+
pass
19+
20+
@staticmethod
21+
def json():
22+
return {'data': '0x'}
23+
24+
25+
@pytest.fixture
26+
def OffchainLookup(w3):
27+
# compiled from `tests/core/contracts/contract_sources/OffchainLookup.sol`
28+
return w3.eth.contract(
29+
abi=json.loads('[{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"string[]","name":"urls","type":"string[]"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes4","name":"callbackFunction","type":"bytes4"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"OffchainLookup","type":"error"},{"inputs":[],"name":"continuousOffchainLookup","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"}]'), # noqa: E501
30+
bytecode="6080604052604051806020016040528060405180606001604052806027815260200161066360279139815250600090600161003b92919061004e565b5034801561004857600080fd5b50610215565b82805482825590600052602060002090810192821561009d579160200282015b8281111561009c57825182908051906020019061008c9291906100ae565b509160200191906001019061006e565b5b5090506100aa9190610134565b5090565b8280546100ba906101e4565b90600052602060002090601f0160209004810192826100dc5760008555610123565b82601f106100f557805160ff1916838001178555610123565b82800160010185558215610123579182015b82811115610122578251825591602001919060010190610107565b5b5090506101309190610158565b5090565b5b80821115610154576000818161014b9190610175565b50600101610135565b5090565b5b80821115610171576000816000905550600101610159565b5090565b508054610181906101e4565b6000825580601f1061019357506101b2565b601f0160209004906000526020600020908101906101b19190610158565b5b50565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806101fc57607f821691505b60208210810361020f5761020e6101b5565b5b50919050565b61043f806102246000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c806309a3c01b14610030575b600080fd5b61003861004e565b6040516100459190610137565b60405180910390f35b606080306000826309a3c01b60e01b846040517f556f18300000000000000000000000000000000000000000000000000000000081526004016100959594939291906103a1565b60405180910390fd5b600081519050919050565b600082825260208201905092915050565b60005b838110156100d85780820151818401526020810190506100bd565b838111156100e7576000848401525b50505050565b6000601f19601f8301169050919050565b60006101098261009e565b61011381856100a9565b93506101238185602086016100ba565b61012c816100ed565b840191505092915050565b6000602082019050818103600083015261015181846100fe565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061018482610159565b9050919050565b61019481610179565b82525050565b600081549050919050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061021257607f821691505b602082108103610225576102246101cb565b5b50919050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b6000815461025e816101fa565b610268818661022b565b945060018216600081146102835760018114610295576102c8565b60ff19831686526020860193506102c8565b61029e8561023c565b60005b838110156102c0578154818901526001820191506020810190506102a1565b808801955050505b50505092915050565b60006102dd8383610251565b905092915050565b6000600182019050919050565b60006102fd8261019a565b61030781856101a5565b935083602082028501610319856101b6565b8060005b858110156103545784840389528161033585826102d1565b9450610340836102e5565b925060208a0199505060018101905061031d565b50829750879550505050505092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61039b81610366565b82525050565b600060a0820190506103b6600083018861018b565b81810360208301526103c881876102f2565b905081810360408301526103dc81866100fe565b90506103eb6060830185610392565b81810360808301526103fd81846100fe565b9050969550505050505056fea2646970667358221220d5f319783573046ffeda63856488c1e5e323df6b931367249ef3d7c41876848364736f6c634300080d003368747470733a2f2f776562332e70792f676174657761792f7b73656e6465727d2f7b646174617d", # noqa: E501
31+
bytecode_runtime="608060405234801561001057600080fd5b506004361061002b5760003560e01c806309a3c01b14610030575b600080fd5b61003861004e565b6040516100459190610137565b60405180910390f35b606080306000826309a3c01b60e01b846040517f556f18300000000000000000000000000000000000000000000000000000000081526004016100959594939291906103a1565b60405180910390fd5b600081519050919050565b600082825260208201905092915050565b60005b838110156100d85780820151818401526020810190506100bd565b838111156100e7576000848401525b50505050565b6000601f19601f8301169050919050565b60006101098261009e565b61011381856100a9565b93506101238185602086016100ba565b61012c816100ed565b840191505092915050565b6000602082019050818103600083015261015181846100fe565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061018482610159565b9050919050565b61019481610179565b82525050565b600081549050919050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061021257607f821691505b602082108103610225576102246101cb565b5b50919050565b600082825260208201905092915050565b60008190508160005260206000209050919050565b6000815461025e816101fa565b610268818661022b565b945060018216600081146102835760018114610295576102c8565b60ff19831686526020860193506102c8565b61029e8561023c565b60005b838110156102c0578154818901526001820191506020810190506102a1565b808801955050505b50505092915050565b60006102dd8383610251565b905092915050565b6000600182019050919050565b60006102fd8261019a565b61030781856101a5565b935083602082028501610319856101b6565b8060005b858110156103545784840389528161033585826102d1565b9450610340836102e5565b925060208a0199505060018101905061031d565b50829750879550505050505092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61039b81610366565b82525050565b600060a0820190506103b6600083018861018b565b81810360208301526103c881876102f2565b905081810360408301526103dc81866100fe565b90506103eb6060830185610392565b81810360808301526103fd81846100fe565b9050969550505050505056fea2646970667358221220d5f319783573046ffeda63856488c1e5e323df6b931367249ef3d7c41876848364736f6c634300080d0033", # noqa: E501
32+
)
33+
34+
35+
@pytest.fixture
36+
def offchain_lookup_contract(
37+
w3, wait_for_block, OffchainLookup, wait_for_transaction, address_conversion_func
38+
):
39+
wait_for_block(w3)
40+
deploy_txn_hash = OffchainLookup.constructor().transact({'gas': 10000000})
41+
deploy_receipt = wait_for_transaction(w3, deploy_txn_hash)
42+
contract_address = address_conversion_func(deploy_receipt['contractAddress'])
43+
44+
bytecode = w3.eth.get_code(contract_address)
45+
assert bytecode == OffchainLookup.bytecode_runtime
46+
deployed_offchain_lookup = OffchainLookup(address=contract_address)
47+
assert deployed_offchain_lookup.address == contract_address
48+
return deployed_offchain_lookup
49+
50+
51+
def test_offchain_lookup_raises_on_continuous_redirect(offchain_lookup_contract, monkeypatch):
52+
def mock_get(*args, **kwargs):
53+
return MockHttpSuccessResponse(*args, **kwargs)
54+
55+
monkeypatch.setattr(requests.Session, 'get', mock_get)
56+
57+
with pytest.raises(TooManyRequests, match="Too many CCIP read redirects"):
58+
offchain_lookup_contract.caller.continuousOffchainLookup()

web3/_utils/async_transactions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ async def async_handle_offchain_lookup(
6969
formatted_sender = to_hex_if_bytes(offchain_lookup_payload['sender']).lower()
7070
formatted_data = to_hex_if_bytes(offchain_lookup_payload['callData']).lower()
7171

72-
if formatted_sender != transaction['to'].lower():
72+
if formatted_sender != to_hex_if_bytes(transaction['to']).lower():
7373
raise ValidationError(
7474
'Cannot handle OffchainLookup raised inside nested call. Returned `sender` '
7575
'value does not equal `to` address in transaction.'

web3/_utils/transactions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ def handle_offchain_lookup(
265265
formatted_sender = to_hex_if_bytes(offchain_lookup_payload['sender']).lower()
266266
formatted_data = to_hex_if_bytes(offchain_lookup_payload['callData']).lower()
267267

268-
if formatted_sender != transaction['to'].lower():
268+
if formatted_sender != to_hex_if_bytes(transaction['to']).lower():
269269
raise ValidationError(
270270
'Cannot handle OffchainLookup raised inside nested call. Returned `sender` '
271271
'value does not equal `to` address in transaction.'

web3/providers/eth_tester/defaults.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ def call_eth_tester(
7979
return getattr(eth_tester, fn_name)(*fn_args, **fn_kwargs)
8080
except TransactionFailed as e:
8181
possible_data = e.args[0]
82-
if isinstance(possible_data, str) and possible_data[:10] == 'b"Uo\\x180\\':
82+
if isinstance(possible_data, str) and possible_data[2:10] == 'Uo\\x180\\':
8383
# EIP-3668 | CCIP Read
84-
# b"Uo\\x180\\" is the function selector for:
84+
# b"Uo\x180" is the first 4 bytes of the keccak hash for:
8585
# OffchainLookup(address,string[],bytes,bytes4,bytes)
8686
parsed_data_as_bytes = ast.literal_eval(possible_data)
8787
data_payload = parsed_data_as_bytes[4:] # everything but the function selector

0 commit comments

Comments
 (0)