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

Run L1 devnet #12

Merged
merged 38 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
923a48b
add terminal handling mini-library
norswap Aug 13, 2023
e8dbe44
add utilities to libroll.py
norswap Aug 13, 2023
bb8855c
remove term utilities from libroll
norswap Aug 13, 2023
c8ca38d
overhaul libroll.run
norswap Aug 13, 2023
dc9446d
use libroll.run_roll_log
norswap Aug 13, 2023
b9d6db9
don't print stack trace on user interrupt
norswap Aug 13, 2023
d41c32d
add debug and extend_exception utilities
norswap Aug 13, 2023
de1bcb3
enable showing stack traces
norswap Aug 13, 2023
1a5c5cb
better exceptions in libroll.run
norswap Aug 13, 2023
2e11697
change optimism reference commit
norswap Aug 13, 2023
c6af645
move parsing instructions to bottom of file
norswap Aug 14, 2023
f2cacc1
minor prints improvements
norswap Aug 14, 2023
bf99023
add more prerequisites check and jq installation logic
norswap Aug 15, 2023
83c9b3c
fix incorrect error messages when not capturing
norswap Aug 15, 2023
34ed587
make extend_exception work with all exceptions
norswap Aug 15, 2023
08f4065
correct package name typo
norswap Aug 15, 2023
5be5ef2
plain sh does not support `source `
norswap Aug 15, 2023
c8d633b
fix URL
norswap Aug 15, 2023
7c32e5d
add success messages
norswap Aug 15, 2023
26b1bfc
fix Yarn detection
norswap Aug 15, 2023
e4864d6
don't require have a private key setup to clone
norswap Aug 15, 2023
54b53a6
wrap comment
norswap Aug 15, 2023
5054cbe
move dependency management to own file
norswap Aug 15, 2023
11cbd17
remove NODE_VNAME
norswap Aug 15, 2023
909235a
add foundry dependency installation
norswap Aug 15, 2023
d30cbf6
add geth to deps
norswap Aug 15, 2023
cec2a08
L1 devnet spinning code — using docker
norswap Aug 15, 2023
90baeda
add background process mgmt & exit hooks libraries
norswap Aug 16, 2023
ecb538f
run the L1 node in the background without docker from roll.py
norswap Aug 16, 2023
101783d
don't need w+
norswap Aug 16, 2023
95c4a9b
python doesn't need + for concatenation
norswap Aug 16, 2023
86792b4
reflect that devnetL1.json is a network config, and move it prior to …
norswap Aug 17, 2023
c6ce46b
add comment explaining timestamp (ir)relevance
norswap Aug 17, 2023
b1dc688
add module-level documentation
norswap Aug 17, 2023
3097019
move optimism repo setup to own file
norswap Aug 17, 2023
b3d7605
lint
norswap Aug 18, 2023
5e5dcdd
explicitly use bash with NVM (to buil optimism monorepo and run NVM s…
norswap Aug 18, 2023
6819762
lint
norswap Aug 18, 2023
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
/__pycache__
/.idea
/venv
/.ruff_cache
/.ruff_cache
/bin
/logs
/db
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

linter: ## Run linter
@ruff check .
.PHONY: linter
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ you can automatically subsidize gas for transactions that match certain criteria

The following dependencies will be checked by `roll.py`:

- `make`
- Some common command line utilities: `make, `curl`, `tar`
- Git
- Go 1.19

Expand All @@ -33,6 +33,9 @@ always for your permission before installing anything outside the current direct

- Node.js 16.x
- Yarn (`npm install -g yarn` — the old one, not yarn v3 aka yarn berry)
- Geth > 1.12.0 (but only if you want to run a devnet L1 node)
- The `jq` command line utility
- Foundry

## Contributing

Expand Down
342 changes: 342 additions & 0 deletions deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,342 @@
"""
This module exposes functions to check the presence, and sometimes install op-stack dependencies.
"""

import os
import re
import shutil
import sys

import libroll as lib
import term


####################################################################################################

def basic_setup():
"""
Does some basic setup (creating directories, modifying path), performing basic checks.
"""
os.makedirs("logs", exist_ok=True)
os.makedirs("bin", exist_ok=True)

lib.append_to_path("bin")

if lib.args.use_ansi_esc and not term.is_well_known_term():
print(
"\nWARNING: Your terminal is weird.\n"
"This may cause it to not handle ANSI escape codes well.\n"
"You can disable them with --no-ansi-esc\n")


####################################################################################################

def check_basic_prerequisites():
"""
check basic prerequisites that we won't offer to install.
"""

if shutil.which("make") is None:
raise Exception(
"Make is not installed. Please install it from your package manager.\n"
"e.g. `brew install make` or `sudo apt install build-essential`")

if shutil.which("git") is None:
raise Exception(
"git is not installed. Please install it from your package manager.\n"
"e.g. `brew install git` or `sudo apt install git`")

if shutil.which("curl") is None:
raise Exception(
"curl is not installed. Please install it from your package manager.\n"
"e.g. `sudo apt install curl`")

if shutil.which("tar") is None:
raise Exception(
"tar is not installed. Please install it from your package manager.\n"
"e.g. `sudo apt install tar`")


####################################################################################################

GO_VERSION = "1.19"
"""Minimum Go version required by the Optimism repository."""


def check_go():
if shutil.which("go") is None:
raise Exception(
f"go is not installed. Please install Go version {GO_VERSION} or higher.")
elif lib.run("get go version", "go version") < GO_VERSION:
raise Exception(
f"Go version is too low. Please update to Go **version {GO_VERSION}** or higher.\n"
"Go is backwards compatible, so your old project will continue to build.")


####################################################################################################

JQ_URL_LINUX = "https://github.com/jqlang/jq/releases/download/jq-1.6/jq-linux64"
"""Link to the jq binary for Linux."""

JQ_URL_MACOS = "https://github.com/jqlang/jq/releases/download/jq-1.6/jq-osx-amd64"
"""Link to the jq binary for macOS."""


def check_or_install_jq():
"""
Checks if jq is installed (either globally or in ./bin), and if not, installs it in ./bin.
"""

if shutil.which("jq") is not None:
# This includes ./bin/jq, as basic_setup() adds ./bin to the path.
return

descr = "install jq"
os.makedirs("bin", exist_ok=True)
if sys.platform not in ("linux", "darwin"):
raise Exception(
f"Unsupported OS for automatic jq installation: {sys.platform}.\n"
"Please install jq manually, make sure it is executable and in $PATH or in ./bin/")

print("Installing jq in bin/jq")

try:
if sys.platform == "linux":
lib.run(descr, f"curl -L {JQ_URL_LINUX} -o bin/jq")
elif sys.platform == "darwin":
lib.run(descr, f"curl -L {JQ_URL_MACOS} -o bin/jq")
lib.chmodx("bin/jq")
except Exception as err:
raise lib.extend_exception(err, prefix="Failed to install jq: ")

print(f"Successfully installed jq as ./bin/jq")


####################################################################################################

# Whether we must instruct to use the proper Node version via NVM or not.
must_nvm_use = False
"""Whether we must instruct to use the proper Node version via `nvm use` or not."""


def cmd_with_node(command: str) -> str:
"""
If required, prepends the command with an ``nvm use`` statement.
"""
if must_nvm_use:
return f". ~/.nvm/nvm.sh; nvm use {NODE_VERSION}; {command}"
else:
return command


####################################################################################################

NODE_VERSION = "v16.16.0"
"""Full node of the node version required by the optimism repository."""

NVM_VERSION = "v0.39.4"
"""A recent NVM version to install if needed."""


def check_or_install_node():
"""
Check if Node.js is installed and is the correct version, otherwise prompts the user to install
it via NVM.
"""

# Check if Node is installed and is the correct version.
if shutil.which("node") is not None:
if lib.run("get node version", "node --version") == NVM_VERSION:
return

# Either Node is not installed, or the current version is not v16.
# global must_nvm_use
global must_nvm_use
must_nvm_use = True

def nvm_install_node():
lib.run(f"install Node {NODE_VERSION}", f". ~/.nvm/nvm.sh; nvm install {NODE_VERSION}")
print(f"Successfully installed Node {NODE_VERSION}")

if os.path.isfile(os.path.expanduser("~/.nvm/nvm.sh")):
# We have NVM, try using required version or installing it.
try:
lib.run(f"use node {NODE_VERSION}", f". ~/.nvm/nvm.sh; nvm use {NODE_VERSION}")
except Exception:
if lib.ask_yes_no(f"Node {NODE_VERSION} is required. NVM is installed. "
f"Install with NVM?"):
nvm_install_node()
else:
raise Exception(f"Node {NODE_VERSION} is required.")
else:
# Install NVM + Node.
nvm_url = f"https://raw.githubusercontent.com/nvm-sh/nvm/{NVM_VERSION}/install.sh"
if lib.ask_yes_no(f"Node {NODE_VERSION} is required. Install NVM + Node?"):
lib.run("install nvm", f"curl -o- {nvm_url} | bash")
nvm_install_node()
else:
raise Exception(f"Node {NODE_VERSION} is required.")


####################################################################################################

def check_or_install_yarn():
"""
Check if Node.js is installed on the current Node and is the correct version, otherwise prompts
the user to install it. This call should always be preceded by a call to
:py:func:`check_or_install_node`.
"""
try:
# can't use `shutil.which`, we need to use proper Node version!
lib.run("get yarn version", cmd_with_node("yarn --version"))
return
except Exception:
pass

if lib.ask_yes_no("Yarn is required. Install?"):
lib.run("install Yarn", cmd_with_node("npm install -g yarn"))
print("Successfully installed Yarn.")
else:
raise Exception("Yarn is required.")


####################################################################################################

def get_foundry_version():
"""
Returns the Foundry version (actually the date of release, which is updated a lot more often).
It's possible to have multiple releases on the same day, but these are commit-tagged, so we
can't compare them.
"""
version_blob = lib.run("get forge version", "forge --version")
match = re.search(r"^forge \d+\.\d+\.\d+ \([0-9a-fA-F]+ (\d{4}-\d\d-\d\d)", version_blob)
return None if match is None else match.group(1)


####################################################################################################

# This is a pretty arbitrary cutoff, this was my old version that worked.
MIN_FORGE_VERSION = "2023-05-23"
"""Minimum supported forge version."""


def check_or_install_foundry():
"""
Verify that foundry is installed and has the correct version, or install it if not.
"""

def check_forge_version():
version = get_foundry_version()
if version is None:
return False
print(version)
return version >= MIN_FORGE_VERSION

# Doing this here, even if Foundry might already be in path, covers the edge case where we
# installed Foundry in a previous run of the tool, but the user didn't restart their shell.
lib.append_to_path(os.path.expanduser("~/.foundry/bin"))

if shutil.which("forge") is not None:
if check_forge_version():
return
if shutil.which("foundryup") is not None:
if lib.ask_yes_no("Forge (Foundry) is outdated, run foundryup to update?"):
lib.run("update foundry", "foundryup")
if check_forge_version():
return
if lib.ask_yes_no("Forge is still outdated, update foundryup?"):
install_foundry()
return
else:
if lib.ask_yes_no("Forge (Foundry) is outdated, install foundryup and update?"):
install_foundry()
return
raise Exception(f"Forge (Foundry) is outdated (expected: > {MIN_FORGE_VERSION}).")
else:
if lib.ask_yes_no(f"Forge (Foundry) is required. Install globally?"):
install_foundry()
else:
raise Exception("Forge is required.")


####################################################################################################

def install_foundry():
"""
Installs foundry globally.
"""
print("Installing Foundry")
lib.run("install foundryup", "curl -L https://foundry.paradigm.xyz | bash")
lib.run("install foundry", "foundryup")
version = get_foundry_version()
print(f"Successfully installed Foundry {version}")


####################################################################################################

MIN_GETH_VERSION = "1.12.0"
"""Minimum supported geth version."""

INSTALL_GETH_VERSION = "1.12.0"
"""Version of geth to install if not found."""

GETH_URL_LINUX = \
"https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.12.0-e501b3b0.tar.gz"
"""Link to geth binary for Linux."""

GETH_URL_MACOS = \
"https://gethstore.blob.core.windows.net/builds/geth-darwin-amd64-1.12.0-e501b3b0.tar.gz"
"""Link to geth binary for MacOS."""


def check_or_install_geth():
"""
Verify that geth is installed and has the correct version, or install it if not.
"""

geth_path = shutil.which("geth")
if geth_path is not None:
# This includes ./bin/jq, as basic_setup() adds ./bin to the path.
version_blob = lib.run(f"get geth version", f"geth version")
match = re.search(r"^Version: (\d+\.\d+\.\d+)", version_blob, flags=re.MULTILINE)
if match is not None:
version = match.group(1)
abspath = os.path.abspath(geth_path)
if version >= MIN_GETH_VERSION:
print(f"Using {abspath} (version: {version})")
return
else:
lib.debug(f"Found {abspath} (version: {version})")

if lib.ask_yes_no(
f"Geth {MIN_GETH_VERSION} is required. Install in ./bin?\n"
"This will overwrite any version of geth that might be in that directory."):
install_geth()
else:
raise Exception(f"Geth missing or wrong version (expected: > {MIN_GETH_VERSION}).")


####################################################################################################

def install_geth():
"""
Installs geth in ./bin (uses the GETH_URL_LINUX and GETH_URL_MACOS constants).
"""
descr = "install geth"
os.makedirs("bin", exist_ok=True)
if sys.platform not in ("linux", "darwin"):
raise Exception(
f"Unsupported OS for automatic geth installation: {sys.platform}.\n"
"Please install geth manually and have it in $PATH or in ./bin/")

try:
if sys.platform == "linux":
lib.run(descr, f"curl -L {GETH_URL_LINUX} | tar xz -C bin --strip-components=1")
elif sys.platform == "darwin":
lib.run(descr, f"curl -L {GETH_URL_MACOS} | tar xz -C bin --strip-components=1")
except Exception as err:
raise lib.extend_exception(err, prefix="Failed to install geth: ")

print(f"Successfully installed geth {INSTALL_GETH_VERSION} as ./bin/geth")

####################################################################################################
Loading
Loading