Skip to content

Commit

Permalink
feat: Use hydrated Anvil at Devnet infrastructure
Browse files Browse the repository at this point in the history
  • Loading branch information
fmoura committed Nov 16, 2023
1 parent 3197e93 commit bc856db
Show file tree
Hide file tree
Showing 16 changed files with 461 additions and 75 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
.DS_Store
build/deployments
**/.idea/
build/anvil/anvil_state/state.json
build/anvil/deployments/**
22 changes: 22 additions & 0 deletions build/anvil/anvil-base/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# syntax=docker.io/docker/dockerfile:1.4
FROM debian:bookworm-20230814-slim

# install curl and jq (for healthcheck support)
RUN <<EOF
apt-get update
DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends ca-certificates curl git jq xxd
rm -rf /var/lib/apt/lists/*
EOF

# download pre-compiled binaries
RUN curl -sSL https://github.com/foundry-rs/foundry/releases/download/nightly/foundry_nightly_linux_$(dpkg --print-architecture).tar.gz | \
tar -zx -C /usr/local/bin

# healthcheck script using net_listening JSON-RPC method
COPY eth_isready /usr/local/bin
COPY eth_dump /usr/local/bin
COPY eth_load /usr/local/bin

HEALTHCHECK CMD eth_isready

CMD ["anvil"]
6 changes: 6 additions & 0 deletions build/anvil/anvil-base/docker-bake.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
target "docker-metadata-action" {}
target "docker-platforms" {}

target "default" {
inherits = ["docker-metadata-action", "docker-platforms"]
}
3 changes: 3 additions & 0 deletions build/anvil/anvil-base/docker-bake.override.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
target "default" {
tags = ["cartesi/anvil:devel"]
}
6 changes: 6 additions & 0 deletions build/anvil/anvil-base/docker-bake.platforms.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
target "docker-platforms" {
platforms = [
"linux/amd64",
"linux/arm64"
]
}
11 changes: 11 additions & 0 deletions build/anvil/anvil-base/eth_dump
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env sh

# dump the state from the JSON-RPC server, convert from the hex anvil returns to text, which is a JSON string
curl \
-sL \
-X POST \
-H 'Content-Type: application/json' \
--data '{"id":1,"jsonrpc":"2.0","method":"anvil_dumpState","params":[]}' "${RPC_URL:-http://127.0.0.1:8545}" | \
jq -r .result| \
cut -c 3- | \
xxd -r -p
4 changes: 4 additions & 0 deletions build/anvil/anvil-base/eth_isready
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh

# https://ethereum.org/en/developers/docs/apis/json-rpc/#net_listening
curl -X POST -s -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":"1","method":"net_listening","params":[]}' ${RPC_URL:-http://127.0.0.1:8545} | jq '.result'
14 changes: 14 additions & 0 deletions build/anvil/anvil-base/eth_load
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env sh
set -e
RPC_URL="${RPC_URL:-http://127.0.0.1:8545}"

# use a large column value so we have everything on one line, even though man says 256 is the maximum
STATE=$(xxd -ps -c 1000000000)

# build the JSON-RPC request, and write to a file because it's too long for the curl call
DATA="{\"id\":2,\"jsonrpc\":\"2.0\",\"method\":\"anvil_loadState\",\"params\":[\"0x$STATE\"]}"
TMPFILE=$(mktemp)
echo "$DATA" > "$TMPFILE"

echo "Loading state into ${RPC}"
curl -sL -H 'Content-Type: application/json' -d @"$TMPFILE" "$RPC_URL" | jq -r .result
10 changes: 10 additions & 0 deletions build/anvil/devnet/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ARG NODE_ANVIL_VERSION=devel
FROM cartesi/anvil:${NODE_ANVIL_VERSION}

WORKDIR /usr/share/cartesi
COPY ./entrypoint.sh /
COPY ./anvil_state/state.json .
COPY ./deployments/localhost.json .

ENTRYPOINT ["/entrypoint.sh"]
CMD ["anvil", "--load-state", "/usr/share/cartesi/state.json"]
6 changes: 6 additions & 0 deletions build/anvil/devnet/docker-bake.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
target "docker-metadata-action" {}
target "docker-platforms" {}

target "default" {
inherits = ["docker-metadata-action", "docker-platforms"]
}
6 changes: 6 additions & 0 deletions build/anvil/devnet/docker-bake.override.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
target "default" {
tags = ["cartesi/anvil_devnet:devel"]
args = {
NODE_ANVIL_VERSION = "devel"
}
}
6 changes: 6 additions & 0 deletions build/anvil/devnet/docker-bake.platforms.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
target "docker-platforms" {
platforms = [
"linux/amd64",
"linux/arm64"
]
}
3 changes: 3 additions & 0 deletions build/anvil/devnet/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh
jq -r '.contracts | to_entries | .[] | "\(.value.address) \(.key)"' < /usr/share/cartesi/localhost.json
exec "$@"
57 changes: 57 additions & 0 deletions build/anvil/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
version: "3.9"

services:

anvil:
build:
context: ./anvil-base
dockerfile: ./Dockerfile
tags:
- "cartesi/anvil:devel"
command:
[
"anvil",
"--host",
"0.0.0.0",
"--dump-state",
"anvil_state.json"
]
healthcheck:
test: ["CMD", "eth_isready"]
interval: 10s
timeout: 1s
retries: 5
ports:
- "8545:8545"
volumes:
- ./devnet/anvil_state:/anvil_state.json

hardhat:
image: cartesi/rollups-hardhat:1.0.0
environment:
- RPC_URL=http://anvil:8545
command:
[
"deploy",
"--network",
"localhost",
"--export",
"/opt/cartesi/share/deployments/localhost.json"
]
init: true
depends_on:
anvil:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"test",
"-f",
"/opt/cartesi/share/deployments/localhost.json"
]
interval: 30s
timeout: 30s
retries: 5
volumes:
- ./devnet/deployments:/opt/cartesi/share/deployments
138 changes: 138 additions & 0 deletions build/anvil/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

import path from "path";
import { HardhatUserConfig } from "hardhat/config";
import { HttpNetworkUserConfig } from "hardhat/types";
import { getSingletonFactoryInfo } from "@safe-global/safe-singleton-factory";

import "@nomiclabs/hardhat-ethers";
import "@nomicfoundation/hardhat-verify";
import "@typechain/hardhat";
import "hardhat-deploy";
import "hardhat-gas-reporter";

import {
Chain,
arbitrum,
arbitrumGoerli,
mainnet,
optimism,
optimismGoerli,
sepolia,
} from "viem/chains";

// read MNEMONIC from env variable
let mnemonic = process.env.MNEMONIC;

const ppath = (packageName: string, pathname: string) => {
return path.join(
path.dirname(require.resolve(`${packageName}/package.json`)),
pathname,
);
};

const networkConfig = (chain: Chain): HttpNetworkUserConfig => {
let url = process.env.RPC_URL || chain.rpcUrls.public.http.at(0);

// support for infura and alchemy URLs through env variables
if (process.env.INFURA_ID && chain.rpcUrls.infura?.http) {
url = `${chain.rpcUrls.infura.http}/${process.env.INFURA_ID}`;
} else if (process.env.ALCHEMY_ID && chain.rpcUrls.alchemy?.http) {
url = `${chain.rpcUrls.alchemy.http}/${process.env.ALCHEMY_ID}`;
}

return {
chainId: chain.id,
url,
accounts: mnemonic ? { mnemonic } : undefined,
};
};

const config: HardhatUserConfig = {
networks: {
hardhat: mnemonic ? { accounts: { mnemonic } } : {},
localhost: {
url: process.env.RPC_URL || "http://192.168.0.192:8545",
accounts: mnemonic ? { mnemonic } : undefined,
},
arbitrum: networkConfig(arbitrum),
arbitrum_goerli: networkConfig(arbitrumGoerli),
mainnet: networkConfig(mainnet),
sepolia: networkConfig(sepolia),
optimism: networkConfig(optimism),
optimism_goerli: networkConfig(optimismGoerli),
},
solidity: {
version: "0.8.19",
settings: {
optimizer: {
enabled: true,
},
},
},
paths: {
artifacts: "artifacts",
deploy: "deploy",
deployments: "deployments",
},
deterministicDeployment: (network: string) => {
// networks will use another deterministic deployment proxy
// https://github.com/safe-global/safe-singleton-factory
const chainId = parseInt(network);
const info = getSingletonFactoryInfo(chainId);
if (info) {
return {
factory: info.address,
deployer: info.signerAddress,
funding: (
BigInt(info.gasPrice) * BigInt(info.gasLimit)
).toString(),
signedTx: info.transaction,
};
} else {
console.warn(
`unsupported deterministic deployment for network ${network}`,
);
return undefined;
}
},
typechain: {
outDir: "src/types",
target: "ethers-v5",
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
external: {
contracts: [
{
artifacts: ppath("@cartesi/util", "/export/artifacts"),
deploy: ppath("@cartesi/util", "/dist/deploy"),
},
],
deployments: {
localhost: ["deployments/localhost"],
arbitrum: [ppath("@cartesi/util", "/deployments/arbitrum")],
arbitrum_goerli: [
ppath("@cartesi/util", "/deployments/arbitrum_goerli"),
],
mainnet: [ppath("@cartesi/util", "/deployments/mainnet")],
optimism: [ppath("@cartesi/util", "/deployments/optimism")],
optimism_goerli: [
ppath("@cartesi/util", "/deployments/optimism_goerli"),
],
sepolia: [ppath("@cartesi/util", "/deployments/sepolia")],
},
},
namedAccounts: {
deployer: {
default: 0,
},
},
gasReporter: {
enabled: process.env.REPORT_GAS ? true : false,
},
};

export default config;
Loading

0 comments on commit bc856db

Please sign in to comment.