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

Implement RPC calls in Jupyter/Colab #124

Merged
merged 36 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
21008ef
Implement RPC calls via the browser
DanielSchiavini Jan 24, 2024
7aaf694
Cleanup console calls
DanielSchiavini Jan 24, 2024
53804fe
Add debug statements
DanielSchiavini Jan 25, 2024
a31c6f5
Add debug statements
DanielSchiavini Jan 25, 2024
59c4715
Use sequential rpc calls
DanielSchiavini Jan 26, 2024
15b4034
Omit PR builds and fork tests without URL
DanielSchiavini Jan 26, 2024
a2f2a2c
Parse error into RPCError
DanielSchiavini Jan 26, 2024
1126dd8
Cache provider, improve errors
DanielSchiavini Jan 26, 2024
635dbfa
Revert network change
DanielSchiavini Jan 26, 2024
0aa4ae7
Check if we have a Sepolia secret
DanielSchiavini Jan 26, 2024
c9d808d
Remove todo
DanielSchiavini Jan 26, 2024
9021e3b
Lint
DanielSchiavini Jan 26, 2024
31911c3
Cover Jupyter plugin with mocked tests
DanielSchiavini Jan 26, 2024
07c9c06
Warning for debugging
DanielSchiavini Jan 29, 2024
1c07f82
Allow browser rpc to be forked
DanielSchiavini Jan 29, 2024
ccbd823
Get rid of ABC base
DanielSchiavini Jan 31, 2024
cca9696
Different error format
DanielSchiavini Jan 31, 2024
59db7d0
Get rid of BrowserEnv
DanielSchiavini Feb 1, 2024
9d1e5c6
Fix tests
DanielSchiavini Feb 2, 2024
d396243
Merge branch 'master' of github.com:vyperlang/titanoboa into 119/brow…
DanielSchiavini Feb 2, 2024
e1ae7e0
Revert CI changes
DanielSchiavini Feb 2, 2024
d9e9274
Self review
DanielSchiavini Feb 2, 2024
35e80d1
Mock tornado
DanielSchiavini Feb 2, 2024
a32809d
Merge branch 'master' of github.com:vyperlang/titanoboa into 119/brow…
DanielSchiavini Feb 2, 2024
65d557f
Review comments
DanielSchiavini Feb 2, 2024
5b58f95
2nd round review
DanielSchiavini Feb 2, 2024
73f8b2f
Add RPC identifier
DanielSchiavini Feb 2, 2024
e3a4b19
Get rid of EthereumRPC private methods
DanielSchiavini Feb 2, 2024
2847d10
Avoid test pollution
DanielSchiavini Feb 2, 2024
1528a3e
Front-end polling
DanielSchiavini Feb 5, 2024
9735953
Merge branch '119/rpc-refactor' into 119/browser-rpc
DanielSchiavini Feb 5, 2024
dc6ab80
Merge branch 'master' of github.com:vyperlang/titanoboa into 119/brow…
DanielSchiavini Feb 7, 2024
acbb919
Move wait_for_tx_receipt to RPC
DanielSchiavini Feb 7, 2024
0bf0348
Review comments
DanielSchiavini Feb 7, 2024
41ec1db
Merge branch 'master' of github.com:vyperlang/titanoboa into 119/brow…
DanielSchiavini Feb 7, 2024
b50a099
Review comments
DanielSchiavini Feb 7, 2024
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
23 changes: 12 additions & 11 deletions boa/integrations/jupyter/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,6 @@ def name(self):
return self.identifier

def fetch(self, method: str, params: Any) -> Any:
if method == "eth_getTransactionReceipt":
# we do the polling in the browser to avoid too many callbacks
# each callback generates currently 10px empty space in the frontend
timeout_ms = CALLBACK_TOKEN_TIMEOUT.total_seconds() * 1000
return _javascript_call(
"waitForTransactionReceipt",
params,
timeout_ms,
timeout_message=RPC_TIMEOUT_MESSAGE,
)

return _javascript_call(
"rpc", method, params, timeout_message=RPC_TIMEOUT_MESSAGE
)
Expand All @@ -101,6 +90,18 @@ def fetch_multi(self, payloads: list[tuple[str, Any]]) -> list[Any]:
"multiRpc", payloads, timeout_message=RPC_TIMEOUT_MESSAGE
)

def wait_for_tx_receipt(self, tx_hash, timeout: float, poll_latency=0.25):
# we do the polling in the browser to avoid too many callbacks
# each callback generates currently 10px empty space in the frontend
timeout_ms, pool_latency_ms = timeout * 1000, poll_latency * 1000
return _javascript_call(
"waitForTransactionReceipt",
tx_hash,
timeout_ms,
pool_latency_ms,
timeout_message=RPC_TIMEOUT_MESSAGE,
)


def _javascript_call(js_func: str, *args, timeout_message: str) -> Any:
"""
Expand Down
2 changes: 1 addition & 1 deletion boa/integrations/jupyter/constants.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import timedelta

NUL = b"\0"
CALLBACK_TOKEN_TIMEOUT = timedelta(minutes=4) # matches default TransactionSettings
CALLBACK_TOKEN_TIMEOUT = timedelta(minutes=3)
SHARED_MEMORY_LENGTH = 50 * 1024 + len(NUL) # Size of the shared memory object
CALLBACK_TOKEN_BYTES = 32
ETHERS_JS_URL = "https://cdnjs.cloudflare.com/ajax/libs/ethers/6.9.0/ethers.umd.min.js"
Expand Down
8 changes: 4 additions & 4 deletions boa/integrations/jupyter/jupyter.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
const rpc = (method, params) => getEthersProvider().send(method, params);

/** Wait until the transaction is mined */
const waitForTransactionReceipt = async (params, timeout, wait = 1000) => {
const waitForTransactionReceipt = async (params, timeout, poll_latency) => {
try {
const result = await rpc('eth_getTransactionReceipt', params);
if (result) {
Expand All @@ -67,11 +67,11 @@
throw err;
}
}
if (timeout < wait) {
if (timeout < poll_latency) {
throw new Error('Timeout waiting for transaction receipt');
}
await sleep(wait);
return waitForTransactionReceipt(params, timeout - wait, wait);
await sleep(poll_latency);
return waitForTransactionReceipt(params, timeout - poll_latency, poll_latency);
charles-cooper marked this conversation as resolved.
Show resolved Hide resolved
};

/** Call multiple RPCs in sequence */
Expand Down
27 changes: 7 additions & 20 deletions boa/network.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# an Environment which interacts with a real (prod or test) chain
import contextlib
import time
import warnings
from dataclasses import dataclass
from functools import cached_property
Expand Down Expand Up @@ -322,24 +321,6 @@ def deploy_code(self, sender=None, gas=None, value=0, bytecode=b"", **kwargs):

return create_address, deployed_bytecode

def _wait_for_tx_trace(self, tx_hash, poll_latency=0.25):
start = time.time()

timeout = self.tx_settings.poll_timeout

while True:
receipt = self._rpc.fetch("eth_getTransactionReceipt", [tx_hash])
if receipt is not None:
break
if time.time() + poll_latency > start + timeout:
raise ValueError(f"Timed out waiting for ({tx_hash})")
time.sleep(poll_latency)

trace = None
if self._tracer is not None:
trace = self._rpc.fetch("debug_traceTransaction", [tx_hash, self._tracer])
return receipt, trace

@cached_property
def _tracer(self):
def _warn_no_tracer():
Expand Down Expand Up @@ -437,7 +418,13 @@ def _send_txn(self, from_, to=None, gas=None, value=None, data=None):
# TODO real logging
print(f"tx broadcasted: {tx_hash}")

receipt, trace = self._wait_for_tx_trace(tx_hash)
receipt = self._rpc.wait_for_tx_receipt(tx_hash, self.tx_settings.poll_timeout)

trace = None
if self._tracer is not None:
trace = self._rpc.fetch_uncached(
"debug_traceTransaction", [tx_hash, self._tracer]
)

print(f"{tx_hash} mined in block {receipt['blockHash']}!")

Expand Down
12 changes: 12 additions & 0 deletions boa/rpc.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import time
from typing import Any
from urllib.parse import urlparse

Expand Down Expand Up @@ -78,6 +79,17 @@ def fetch(self, method: str, params: Any) -> Any:
def fetch_multi(self, payloads: list[tuple[str, Any]]) -> list[Any]:
raise NotImplementedError

def wait_for_tx_receipt(self, tx_hash, timeout: float, poll_latency=0.25):
start = time.time()

while True:
receipt = self.fetch_uncached("eth_getTransactionReceipt", [tx_hash])
if receipt is not None:
return receipt
if time.time() + poll_latency > start + timeout:
raise ValueError(f"Timed out waiting for ({tx_hash})")
time.sleep(poll_latency)


class EthereumRPC(RPC):
charles-cooper marked this conversation as resolved.
Show resolved Hide resolved
def __init__(self, url: str):
Expand Down
Loading