├── lighthouse
├── deploy_block.txt
├── Dockerfile.lighthouse
├── run_bootnode.sh
├── run_beacon_node.sh
├── setup.sh
└── generate-genesis.sh
├── geth
├── .dockerignore
├── bootnode.sh
├── Dockerfile.geth
├── geth.sh
└── geth-genesis.json
├── lodestar
├── .dockerignore
├── run_beacon_node.sh
└── Dockerfile.lodestar
├── eth.png
├── prysm
├── .dockerignore
├── Dockerfile.prysm
└── run_beacon_node.sh
├── .gitignore
├── shared
├── jwtsecret
├── genesis-generator-configs
│ ├── cl
│ │ ├── mnemonics.yaml
│ │ └── config.yaml
│ ├── el
│ │ └── genesis-config.yaml
│ └── values.env
├── util.go
├── run_genesis_generator.sh
├── blobs.go
├── config.go
└── genesis.json
├── blob_tx_generator
├── README.md
├── package.json
└── blob.js
├── point_evaluation_tx
├── README.md
├── package.json
├── PointEvaluationTest.sol
├── compile.js
└── pointEvaluationTest.js
├── besu
├── besu.sh
├── Dockerfile.besu
└── log4j2.xml
├── .gitmodules
├── nethermind
└── nethermind.sh
├── erigon
├── Dockerfile.erigon
└── erigon.sh
├── tests
├── ctrl
│ ├── ctrl.go
│ ├── services.go
│ └── bootstrap.go
├── pre-4844
│ └── main.go
├── blobtx
│ └── main.go
├── util
│ ├── util.go
│ └── download.go
├── initial-sync
│ └── main.go
└── fee-market
│ └── main.go
├── Makefile
├── README.md
├── upload
└── main.go
├── download
└── main.go
├── .github
└── workflows
│ └── ci.yml
├── go.mod
└── docker-compose.yml
/lighthouse/deploy_block.txt:
--------------------------------------------------------------------------------
1 | 0
2 |
--------------------------------------------------------------------------------
/geth/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | .github
3 | **/*_tests.go
4 |
--------------------------------------------------------------------------------
/lodestar/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | .github
3 | node_modules
4 |
--------------------------------------------------------------------------------
/eth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Inphi/eip4844-interop/HEAD/eth.png
--------------------------------------------------------------------------------
/prysm/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | .github
3 | **/bazel*
4 | **/*_tests.go
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | .vscode/
3 | node_modules/
4 | shared/generated-configs
5 |
--------------------------------------------------------------------------------
/shared/jwtsecret:
--------------------------------------------------------------------------------
1 | 0xfad2709d0bb03bf0e8ba3c99bea194575d3e98863133d1af638ed056d1d59345
--------------------------------------------------------------------------------
/shared/genesis-generator-configs/cl/mnemonics.yaml:
--------------------------------------------------------------------------------
1 | - mnemonic: "${EL_AND_CL_MNEMONIC}" # a 24 word BIP 39 mnemonic
2 | count: $NUMBER_OF_VALIDATORS
3 |
--------------------------------------------------------------------------------
/blob_tx_generator/README.md:
--------------------------------------------------------------------------------
1 | # How to use
2 |
3 | 1. Run `npm i` to install dependencies
4 | 1. Run `node blob.js "some test data"` to generate a blob transaction and send it to Geth
5 |
--------------------------------------------------------------------------------
/point_evaluation_tx/README.md:
--------------------------------------------------------------------------------
1 | # EIP 4844 Point Evaluation Precompile Transaction Test
2 |
3 | 1. Run `yarn` to install dependencies
4 | 1. Run `node pointEvaluationTest.js "point evaluation input hexstring ` which will call with evaluation point precompile with the given input.
5 |
--------------------------------------------------------------------------------
/point_evaluation_tx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "point_evaluation_tx",
3 | "version": "1.0.0",
4 | "main": "main.js",
5 | "license": "MIT",
6 | "devDependencies": {
7 | "chai": "^4.3.6",
8 | "ethereum-waffle": "^3.4.4",
9 | "ethers": "^5.6.8",
10 | "mocha": "^10.0.0",
11 | "solc": "^0.8.15"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/geth/bootnode.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | priv_key="02fd74636e96a8ffac8e7b01b0de8dea94d6bcf4989513b38cf59eb32163ff91"
4 | ip addr show eth0
5 | EXTERNAL_IP=$(ip addr show eth0 | grep inet | awk '{ print $2 }' | cut -d '/' -f1)
6 |
7 | #bootnode -nat extip:${EXTERNAL_IP} -nodekeyhex $priv_key
8 | bootnode -nodekeyhex $priv_key -addr ${EXTERNAL_IP}:30301 -verbosity 9
9 |
--------------------------------------------------------------------------------
/point_evaluation_tx/PointEvaluationTest.sol:
--------------------------------------------------------------------------------
1 | //SPDX-License-Identifier: Unlicense
2 | pragma solidity ^0.8.0;
3 |
4 | contract PointEvaluationTest {
5 | constructor(bytes memory input) {
6 | assembly {
7 | if iszero(staticcall(gas(), 0x14, mload(input), 0xc0, 0, 0)) {
8 | revert(0,0)
9 | }
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/blob_tx_generator/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "blob_tx_generator",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "blob.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "axios": "^0.27.2",
13 | "ethers": "^5.6.8"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/besu/besu.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | LOG4J_CONFIGURATION_FILE=/opt/besu/log4j2.xml \
4 | /opt/besu/bin/besu \
5 | --data-path=/data \
6 | --genesis-file=/config_data/custom_config_data/besu.json \
7 | --rpc-http-enabled=true \
8 | --rpc-http-host="0.0.0.0" \
9 | --rpc-http-port=8545 \
10 | --engine-rpc-enabled=true \
11 | --engine-rpc-port=8551 \
12 | --engine-host-allowlist="*" \
13 | --rpc-http-cors-origins="*" \
14 | --host-allowlist="*" \
15 | --p2p-enabled=true \
16 | --sync-mode="FULL" \
17 | --data-storage-format="BONSAI" \
18 | --engine-jwt-secret=/config_data/el/jwtsecret
19 |
--------------------------------------------------------------------------------
/point_evaluation_tx/compile.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 | const solc = require('solc');
4 |
5 | const source = fs.readFileSync("./PointEvaluationTest.sol", 'utf8');
6 |
7 | const input = {
8 | language: 'Solidity',
9 | sources: {
10 | 'PointEvaluationTest.sol': {
11 | content: source,
12 | },
13 | },
14 | settings: {
15 | outputSelection: {
16 | '*': {
17 | '*': ['*'],
18 | },
19 | },
20 | },
21 | };
22 |
23 | module.exports = JSON.parse(solc.compile(JSON.stringify(input))).contracts[
24 | 'PointEvaluationTest.sol'
25 | ].PointEvaluationTest;
26 |
--------------------------------------------------------------------------------
/shared/util.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "time"
7 |
8 | "github.com/ethereum/go-ethereum"
9 | "github.com/ethereum/go-ethereum/common"
10 | "github.com/ethereum/go-ethereum/core/types"
11 | "github.com/ethereum/go-ethereum/ethclient"
12 | )
13 |
14 | func WaitForReceipt(ctx context.Context, client *ethclient.Client, txhash common.Hash) (*types.Receipt, error) {
15 | for {
16 | receipt, err := client.TransactionReceipt(ctx, txhash)
17 | if err == ethereum.NotFound {
18 | time.Sleep(time.Second * 1)
19 | continue
20 | }
21 | if err != nil {
22 | return nil, fmt.Errorf("%w: TransactionReceipt", err)
23 | }
24 | return receipt, nil
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/shared/genesis-generator-configs/el/genesis-config.yaml:
--------------------------------------------------------------------------------
1 | chain_id: ${CHAIN_ID}
2 | deposit_contract_address: "${DEPOSIT_CONTRACT_ADDRESS}"
3 | mnemonic: ${EL_AND_CL_MNEMONIC}
4 | el_premine:
5 | "m/44'/60'/0'/0/0": 1000000000ETH
6 | "m/44'/60'/0'/0/1": 1000000000ETH
7 | "m/44'/60'/0'/0/2": 1000000000ETH
8 | "m/44'/60'/0'/0/3": 1000000000ETH
9 | "m/44'/60'/0'/0/4": 1000000000ETH
10 | "m/44'/60'/0'/0/5": 1000000000ETH
11 | el_premine_addrs:
12 | "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": "1000000000ETH"
13 | genesis_timestamp: ${GENESIS_TIMESTAMP}
14 | genesis_delay: ${GENESIS_DELAY}
15 | capella_fork_epoch: ${CAPELLA_FORK_EPOCH}
16 | eip4844_fork_epoch: ${EIP4844_FORK_EPOCH}
17 | slots_per_epoch: ${SLOTS_PER_EPOCH}
18 | seconds_per_slot: ${SECONDS_PER_SLOT}
19 |
--------------------------------------------------------------------------------
/besu/Dockerfile.besu:
--------------------------------------------------------------------------------
1 | FROM gradle:7.6.0 as builder
2 |
3 | COPY besu/besu /src/besu
4 | COPY .git/modules/besu/besu /.git/modules/besu/besu
5 |
6 | # trick to avoid running the long downloadLicenses task
7 | RUN mkdir -p /src/besu/build/reports/license/ && \
8 | touch /src/besu/build/reports/license/license-dependency.xml
9 |
10 | WORKDIR /src/besu
11 |
12 | RUN ./gradlew --no-daemon installDist
13 |
14 | FROM eclipse-temurin:17
15 |
16 | COPY --from=builder /src/besu/build/install/besu /opt/besu/
17 |
18 | RUN adduser --disabled-password --gecos "" --home /opt/besu besu && \
19 | mkdir /data && \
20 | chown besu:besu /opt/besu /data
21 |
22 | USER besu
23 |
24 | EXPOSE 8545 8546 8547 8550 8551 30303
25 |
26 | WORKDIR /data
27 |
28 | ENTRYPOINT ["/opt/besu/bin/besu"]
--------------------------------------------------------------------------------
/lighthouse/Dockerfile.lighthouse:
--------------------------------------------------------------------------------
1 | FROM rust:1.65.0-bullseye AS builder
2 | RUN apt-get update && apt-get -y upgrade && apt-get install -y cmake clang libclang-dev protobuf-compiler
3 | COPY lighthouse lighthouse
4 | RUN cd lighthouse && make && make install-lcli
5 |
6 | FROM ubuntu:22.04
7 | RUN apt-get update && apt-get install -y software-properties-common && add-apt-repository ppa:rmescandon/yq
8 | RUN apt-get update && apt-get -y upgrade && apt-get install -y --no-install-recommends \
9 | libssl-dev \
10 | ca-certificates \
11 | curl \
12 | iproute2 \
13 | jq \
14 | yq \
15 | && apt-get clean \
16 | && rm -rf /var/lib/apt/lists/*
17 | COPY --from=builder /usr/local/cargo/bin/lighthouse /usr/local/bin/lighthouse
18 | COPY --from=builder /usr/local/cargo/bin/lcli /usr/local/bin/lcli
19 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "geth/go-ethereum"]
2 | path = geth/go-ethereum
3 | url = https://github.com/mdehoog/go-ethereum
4 | branch = eip-4844
5 | [submodule "prysm/prysm"]
6 | path = prysm/prysm
7 | url = https://github.com/prysmaticlabs/prysm
8 | branch = eip4844
9 | [submodule "lodestar/lodestar"]
10 | path = lodestar/lodestar
11 | url = https://github.com/chainsafe/lodestar
12 | branch = dapplion/eip-4844
13 | [submodule "erigon/erigon"]
14 | path = erigon/erigon
15 | url = https://github.com/roberto-bayardo/erigon
16 | branch = eip-4844
17 | [submodule "lighthouse/lighthouse"]
18 | path = lighthouse/lighthouse
19 | url = https://github.com/sigp/lighthouse
20 | branch = eip4844
21 | [submodule "besu/besu"]
22 | path = besu/besu
23 | url = https://github.com/hyperledger/besu.git
24 | branch = eip-4844-interop
25 |
--------------------------------------------------------------------------------
/nethermind/nethermind.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -x
4 |
5 | ./Nethermind.Runner \
6 | --datadir="/db" \
7 | --Init.ChainSpecPath="/config_data/custom_config_data/chainspec.json" \
8 | --Init.WebSocketsEnabled=true \
9 | --JsonRpc.Enabled=true \
10 | --JsonRpc.EnabledModules="net,eth,consensus,subscribe,web3,admin,txpool,debug,trace" \
11 | --JsonRpc.EngineEnabledModules="net,eth,consensus,subscribe,web3,admin,txpool,debug,trace" \
12 | --JsonRpc.Port=8545 \
13 | --JsonRpc.WebSocketsPort=8546 \
14 | --JsonRpc.EnginePort=8551 \
15 | --JsonRpc.Host=0.0.0.0 \
16 | --JsonRpc.EngineHost=0.0.0.0 \
17 | --Network.DiscoveryPort=30303 \
18 | --Network.P2PPort=30303 \
19 | --Merge.SecondsPerSlot=3 \
20 | --Init.IsMining=true \
21 | --JsonRpc.JwtSecretFile=/config_data/el/jwtsecret \
22 | --Sync.FastSync=false \
23 | --JsonRpc.MaxBatchSize=1000 \
24 | --JsonRpc.MaxBatchResponseBodySize=1000000000 \
25 | --config none.cfg
26 |
--------------------------------------------------------------------------------
/prysm/Dockerfile.prysm:
--------------------------------------------------------------------------------
1 | FROM golang:1.18 as builder
2 |
3 | RUN apt-get update
4 |
5 | RUN go install github.com/bazelbuild/bazelisk@latest
6 |
7 | WORKDIR /app/prysm
8 |
9 | COPY ./prysm /app/prysm
10 |
11 | # The flag below may be needed if blst throws SIGILL, which happens with certain (older) CPUs
12 | # ENV CGO_CFLAGS="-O -D__BLST_PORTABLE__"
13 | ENV CGO_CFLAGS=$CGO_CFLAGS
14 |
15 | RUN apt-get install patch
16 | RUN bazelisk build //cmd/beacon-chain //cmd/validator
17 |
18 | # We can't glob in COPY. So we cp the binaries to a known location so the next stage can easily access it
19 | RUN cp bazel-out/*/bin/cmd/beacon-chain/beacon-chain_/beacon-chain /usr/local/bin/
20 | RUN cp bazel-out/*/bin/cmd/validator/validator_/validator /usr/local/bin/
21 |
22 | FROM ubuntu:20.04
23 | RUN apt-get update && apt-get install -y curl jq iproute2
24 |
25 | COPY --from=builder /usr/local/bin/beacon-chain /usr/local/bin/
26 | COPY --from=builder /usr/local/bin/validator /usr/local/bin/
27 |
28 | WORKDIR /usr/local/bin
29 | EXPOSE 3500 4000 7500 13000 12000 8080
30 |
--------------------------------------------------------------------------------
/lodestar/run_beacon_node.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | : "${EXECUTION_NODE_URL:-}"
4 | : "${VERBOSITY:-info}"
5 |
6 | EXTERNAL_IP=$(ip addr show eth0 | grep inet | awk '{ print $2 }' | cut -d '/' -f1)
7 |
8 | BOOTNODE=$(cat /config_data/custom_config_data/boot_enr.yaml | sed 's/- //')
9 |
10 | set -x
11 | # https://chainsafe.github.io/lodestar/usage/local/
12 | node ./packages/cli/bin/lodestar beacon \
13 | --logLevel verbose \
14 | --paramsFile /config_data/custom_config_data/config.yaml \
15 | --genesisStateFile /config_data/custom_config_data/genesis.ssz \
16 | --dataDir /chaindata \
17 | --jwt-secret /config_data/cl/jwtsecret \
18 | --execution.urls "$EXECUTION_NODE_URL" \
19 | --network.connectToDiscv5Bootnodes \
20 | --sync.isSingleNode \
21 | --subscribeAllSubnets true \
22 | --bootnodes "$BOOTNODE" \
23 | --enr.ip "$EXTERNAL_IP" \
24 | --port 13000 \
25 | --rest \
26 | --rest.address 0.0.0.0 \
27 | --rest.port 3500 \
28 | --rest.namespace "*" \
29 | --metrics \
30 | --suggestedFeeRecipient 0x8A04d14125D0FDCDc742F4A05C051De07232EDa4
31 |
--------------------------------------------------------------------------------
/besu/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | INFO
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/point_evaluation_tx/pointEvaluationTest.js:
--------------------------------------------------------------------------------
1 | const { solidity } = require('ethereum-waffle')
2 | const chai = require("chai");
3 | const ethers = require("ethers");
4 |
5 | const { abi, evm } = require('./compile');
6 |
7 |
8 | const GAS_CONFIG = {gasPrice: 100000, gasLimit: 1000000};
9 | const EXECUTION_NODE_RPC = "http://localhost:8545";
10 | const PRIVATE_KEY = "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8";
11 |
12 |
13 | async function main(pointEvaluationInput) {
14 | chai.use(solidity);
15 | let provider = await new ethers.providers.JsonRpcProvider(EXECUTION_NODE_RPC);
16 | let signer = new ethers.Wallet(PRIVATE_KEY, provider);
17 |
18 | const PointEvaluationTest= await new ethers.ContractFactory(abi, evm.bytecode.object, signer);
19 | console.log("Calling Point Evaluation Precompile")
20 |
21 | tx = await PointEvaluationTest.deploy(pointEvaluationInput, GAS_CONFIG)
22 | await chai.expect(tx.deployed()).to.not.be.reverted;
23 |
24 | console.log("Point Evaluation Test Passed")
25 |
26 | }
27 |
28 | main(process.argv[2]).catch((error) => {
29 | console.error(error);
30 | process.exitCode = 1;
31 | });
--------------------------------------------------------------------------------
/lodestar/Dockerfile.lodestar:
--------------------------------------------------------------------------------
1 | # --platform=$BUILDPLATFORM is used build javascript source with host arch
2 | # Otherwise TS builds on emulated archs and can be extremely slow (+1h)
3 | FROM --platform=${BUILDPLATFORM:-amd64} node:18-alpine as build_src
4 | WORKDIR /usr/app
5 | RUN apk update && apk add --no-cache g++ make python3 && rm -rf /var/cache/apk/*
6 |
7 | COPY ./lodestar .
8 |
9 | RUN yarn install --non-interactive --frozen-lockfile && \
10 | yarn build && \
11 | yarn install --non-interactive --frozen-lockfile --production
12 |
13 | # Copy built src + node_modules to a new layer to prune unnecessary fs
14 | # Previous layer weights 7.25GB, while this final 488MB (as of Oct 2020)
15 | FROM node:18-alpine
16 |
17 | RUN apk update && apk add curl sed
18 |
19 | WORKDIR /usr/app
20 | COPY --from=build_src /usr/app .
21 |
22 | # NodeJS applications have a default memory limit of 2.5GB.
23 | # This limit is bit tight for a Prater node, it is recommended to raise the limit
24 | # since memory may spike during certain network conditions.
25 | ENV NODE_OPTIONS=--max-old-space-size=4096
26 |
27 | ENTRYPOINT ["node", "./packages/cli/bin/lodestar"]
28 |
29 | EXPOSE 3500 4000 7500 13000 12000 8080
30 |
--------------------------------------------------------------------------------
/lighthouse/run_bootnode.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | #
4 | # Generates a bootnode enr and saves it in $TESTNET/boot_enr.yaml
5 | # Starts a bootnode from the generated enr.
6 | #
7 |
8 | set -Eeuo pipefail
9 |
10 | source /config/values.env
11 |
12 | echo "Generating bootnode enr"
13 |
14 | EXTERNAL_IP=$(ip addr show eth0 | grep inet | awk '{ print $2 }' | cut -d '/' -f1)
15 | BOOTNODE_PORT=4242
16 |
17 | lcli \
18 | generate-bootnode-enr \
19 | --ip $EXTERNAL_IP \
20 | --udp-port $BOOTNODE_PORT \
21 | --tcp-port $BOOTNODE_PORT \
22 | --genesis-fork-version $GENESIS_FORK_VERSION \
23 | --output-dir /data/bootnode
24 |
25 | bootnode_enr=`cat /data/bootnode/enr.dat`
26 | echo "- $bootnode_enr" > /config_data/custom_config_data/boot_enr.yaml
27 | # overwrite the static bootnode file too
28 | echo "- $bootnode_enr" > /config_data/custom_config_data/boot_enr.txt
29 |
30 | echo "Generated bootnode enr - $bootnode_enr"
31 |
32 | DEBUG_LEVEL=${1:-info}
33 |
34 | echo "Starting bootnode"
35 |
36 | exec lighthouse boot_node \
37 | --testnet-dir /config_data/custom_config_data \
38 | --port $BOOTNODE_PORT \
39 | --listen-address 0.0.0.0 \
40 | --disable-packet-filter \
41 | --network-dir /data/bootnode \
42 |
--------------------------------------------------------------------------------
/shared/genesis-generator-configs/values.env:
--------------------------------------------------------------------------------
1 | export CHAIN_ID="42424243"
2 | export DEPOSIT_CONTRACT_ADDRESS="0x4242424242424242424242424242424242424242"
3 | export EL_AND_CL_MNEMONIC="ball wing grant zero upon brave kind cube start pass evoke domain tell length badge deliver divide payment because section mistake equal claim company"
4 | export CL_EXEC_BLOCK="0"
5 | export DEPOSIT_CONTRACT_BLOCK="0"
6 | export NUMBER_OF_VALIDATORS=64
7 | export GENESIS_FORK_VERSION="0x10000040"
8 | export ALTAIR_FORK_VERSION="0x20000040"
9 | export BELLATRIX_FORK_VERSION="0x30000040"
10 | export CAPELLA_FORK_VERSION="0x40000040"
11 | export CAPELLA_FORK_EPOCH="1"
12 | export EIP4844_FORK_VERSION="0x50000040"
13 | export EIP4844_FORK_EPOCH="2"
14 | export WITHDRAWAL_TYPE="0x00"
15 | export WITHDRAWAL_ADDRESS=0xf97e180c050e5Ab072211Ad2C213Eb5AEE4DF134
16 | export BEACON_STATIC_ENR="enr:-Iq4QMCTfIMXnow27baRUb35Q8iiFHSIDBJh6hQM5Axohhf4b6Kr_cOCu0htQ5WvVqKvFgY28893DHAg8gnBAXsAVqmGAX53x8JggmlkgnY0gmlwhLKAlv6Jc2VjcDI1NmsxoQK6S-Cii_KmfFdUJL2TANL3ksaKUnNXvTCv1tLwXs0QgIN1ZHCCIyk"
17 | export GENESIS_TIMESTAMP=1674640628
18 | export GENESIS_DELAY=60
19 | # this must be the same as mainnet's as lighthouse doesn't support a differen value - https://github.com/sigp/lighthouse/issues/3033
20 | export SLOTS_PER_EPOCH=32
21 | export SECONDS_PER_SLOT=3
22 |
--------------------------------------------------------------------------------
/erigon/Dockerfile.erigon:
--------------------------------------------------------------------------------
1 | FROM golang:1.18-alpine3.15 as builder
2 |
3 | COPY ./erigon/go.mod /app/erigon/
4 |
5 | WORKDIR /app/erigon
6 |
7 | RUN go mod download
8 |
9 | RUN apk add --no-cache make gcc g++ musl-dev linux-headers git
10 |
11 | COPY ./erigon /app/erigon
12 |
13 | WORKDIR /app/erigon
14 |
15 | # The flag below may be needed if blst throws SIGILL, which happens with certain (older) CPUs
16 | # ENV CGO_CFLAGS="-O -D__BLST_PORTABLE__"
17 | ENV CGO_CFLAGS=$CGO_CFLAGS
18 |
19 | # Build directly as make erigon doesn't work because it expects a normal git repository rather than a submodule. We set -buildvcs directly here to avoid this
20 | RUN go build \
21 | -ldflags "-extldflags -Wl,-z,stack-size=0x800000" \
22 | -buildvcs=false \
23 | -o build/bin/erigon \
24 | ./cmd/erigon
25 |
26 | #RUN go build \
27 | # -ldflags "-extldflags -Wl,-z,stack-size=0x800000" \
28 | # -buildvcs=false \
29 | # -o build/bin/bootnode \
30 | # ./cmd/bootnode
31 |
32 | # Pull erigon into a second stage deploy alpine container
33 | FROM alpine:3.15
34 |
35 | RUN apk add --no-cache ca-certificates curl jq libgcc libstdc++ bind-tools
36 | COPY --from=builder /app/erigon/build/bin/erigon /usr/local/bin/
37 | #COPY --from=builder /app/go-ethereum/build/bin/bootnode /usr/local/bin/
38 |
39 | WORKDIR /usr/local/bin/
40 | EXPOSE 8545 8546 8547 30303/udp
41 |
--------------------------------------------------------------------------------
/geth/Dockerfile.geth:
--------------------------------------------------------------------------------
1 | FROM golang:1.18-alpine3.15 as builder
2 |
3 | COPY ./go-ethereum/go.mod /app/go-ethereum/
4 |
5 | WORKDIR /app/go-ethereum
6 |
7 | RUN go mod download
8 |
9 | RUN apk add --no-cache make gcc musl-dev linux-headers git
10 |
11 | COPY ./go-ethereum /app/go-ethereum
12 |
13 | WORKDIR /app/go-ethereum
14 |
15 | # The flag below may be needed if blst throws SIGILL, which happens with certain (older) CPUs
16 | # ENV CGO_CFLAGS="-O -D__BLST_PORTABLE__"
17 | ENV CGO_CFLAGS=$CGO_CFLAGS
18 |
19 | # Build directly as make geth doesn't work because it expects a normal git repository rather than a submodule. We set -buildvcs directly here to avoid this
20 | RUN go build \
21 | -ldflags "-extldflags -Wl,-z,stack-size=0x800000" \
22 | -buildvcs=false \
23 | -o build/bin/geth \
24 | ./cmd/geth
25 |
26 | RUN go build \
27 | -ldflags "-extldflags -Wl,-z,stack-size=0x800000" \
28 | -buildvcs=false \
29 | -o build/bin/bootnode \
30 | ./cmd/bootnode
31 |
32 | # Pull Geth into a second stage deploy alpine container
33 | FROM alpine:3.15
34 |
35 | RUN apk add --no-cache ca-certificates curl jq bind-tools
36 | COPY --from=builder /app/go-ethereum/build/bin/geth /usr/local/bin/
37 | COPY --from=builder /app/go-ethereum/build/bin/bootnode /usr/local/bin/
38 |
39 | WORKDIR /usr/local/bin/
40 | EXPOSE 8545 8546 8547 8551 30303/udp
41 |
--------------------------------------------------------------------------------
/shared/run_genesis_generator.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -exu -o pipefail
4 |
5 | source /config/values.env
6 |
7 | if [ -d /tmp/validator-output ]; then
8 | echo "genesis generator already run"
9 | exit 0
10 | fi
11 |
12 | eth2-val-tools keystores\
13 | --insecure --prysm-pass="prysm"\
14 | --out-loc=/tmp/validator-output\
15 | --source-max=64\
16 | --source-min=0\
17 | --source-mnemonic="$EL_AND_CL_MNEMONIC"
18 |
19 | # reset the GENESIS_TIMESTAMP so the chain starts shortly after
20 | DATE=$(date +%s)
21 | sed -i "s/export GENESIS_TIMESTAMP=.*/export GENESIS_TIMESTAMP=$DATE/" /config/values.env
22 |
23 | # this writes generated configs to /data
24 | /work/entrypoint.sh all
25 |
26 | # copy configuration used by the test setup
27 | cp -r /data/* /gen-configs
28 |
29 | cp -r /tmp/validator-output/keys /lighthouse_data/validators
30 | cp -r /tmp/validator-output/secrets /lighthouse_data/secrets
31 |
32 | mkdir -p /prysm_data/wallet/direct/accounts
33 | echo "prysm" > /prysm_data/wallet_pass.txt
34 | cp -r /tmp/validator-output/prysm/direct/accounts/all-accounts.keystore.json /prysm_data/wallet/direct/accounts/all-accounts.keystore.json
35 | cp -r /tmp/validator-output/prysm/keymanageropts.json /prysm_data/wallet/direct/keymanageropts.json
36 |
37 | cp -r /tmp/validator-output/keys /lodestar_data/keystores
38 | cp -r /tmp/validator-output/lodestar-secrets /lodestar_data/secrets
39 |
40 | cp -r /tmp/validator-output/teku-keys /teku_data/keys
41 | cp -r /tmp/validator-output/teku-secrets /teku_data/secrets
42 |
--------------------------------------------------------------------------------
/prysm/run_beacon_node.sh:
--------------------------------------------------------------------------------
1 | #!/bin/env bash
2 |
3 | set -exu -o pipefail
4 |
5 | : "${EXECUTION_NODE_URL:-}"
6 | : "${PROCESS_NAME:-beacon-node}"
7 | : "${TRACING_ENDPOINT:-}"
8 | : "${VERBOSITY:-info}"
9 | : "${P2P_TCP_PORT:-13000}"
10 | : "${MIN_SYNC_PEERS:-0}"
11 |
12 | EXTERNAL_IP=$(ip addr show eth0 | grep inet | awk '{ print $2 }' | cut -d '/' -f1)
13 |
14 | BOOTNODE=$(cat /config_data/custom_config_data/boot_enr.yaml | sed 's/- //')
15 | openssl rand -hex 32 | tr -d '\n' > /tmp/priv-key
16 |
17 | beacon-chain\
18 | --accept-terms-of-use \
19 | --verbosity="$VERBOSITY" \
20 | --datadir /chaindata \
21 | --force-clear-db \
22 | --bootstrap-node $BOOTNODE \
23 | --genesis-state=/config_data/custom_config_data/genesis.ssz \
24 | --execution-endpoint="$EXECUTION_NODE_URL" \
25 | --jwt-secret=/config_data/cl/jwtsecret \
26 | --chain-config-file=/config_data/custom_config_data/config.yaml \
27 | --contract-deployment-block 0 \
28 | --deposit-contract 0x4242424242424242424242424242424242424242 \
29 | --rpc-host 0.0.0.0 \
30 | --rpc-port 4000 \
31 | --grpc-gateway-host 0.0.0.0 \
32 | --grpc-gateway-port 3500 \
33 | --enable-debug-rpc-endpoints \
34 | --min-sync-peers "$MIN_SYNC_PEERS" \
35 | --p2p-local-ip 0.0.0.0 \
36 | --p2p-host-ip "$EXTERNAL_IP" \
37 | --p2p-tcp-port $P2P_TCP_PORT \
38 | --p2p-priv-key=/tmp/priv-key \
39 | --suggested-fee-recipient=0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b \
40 | --subscribe-all-subnets \
41 | --enable-tracing \
42 | --tracing-endpoint "$TRACING_ENDPOINT" \
43 | --tracing-process-name "$PROCESS_NAME" $@
44 |
--------------------------------------------------------------------------------
/shared/blobs.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "github.com/ethereum/go-ethereum/core/types"
5 | "github.com/ethereum/go-ethereum/params"
6 | )
7 |
8 | func EncodeBlobs(data []byte) types.Blobs {
9 | blobs := []types.Blob{{}}
10 | blobIndex := 0
11 | fieldIndex := -1
12 | for i := 0; i < len(data); i += 31 {
13 | fieldIndex++
14 | if fieldIndex == params.FieldElementsPerBlob {
15 | blobs = append(blobs, types.Blob{})
16 | blobIndex++
17 | fieldIndex = 0
18 | }
19 | max := i + 31
20 | if max > len(data) {
21 | max = len(data)
22 | }
23 | copy(blobs[blobIndex][fieldIndex][:], data[i:max])
24 | }
25 | return blobs
26 | }
27 |
28 | // DecodeFlatBlob decodes a flattened blob
29 | func DecodeFlatBlob(blob []byte) []byte {
30 | if len(blob) != params.FieldElementsPerBlob*32 {
31 | panic("invalid blob encoding")
32 | }
33 | var data []byte
34 |
35 | // XXX: the following removes trailing 0s in each field element (see EncodeBlobs), which could be unexpected for certain blobs
36 | j := 0
37 | for i := 0; i < params.FieldElementsPerBlob; i++ {
38 | data = append(data, blob[j:j+31]...)
39 | j += 32
40 | }
41 |
42 | i := len(data) - 1
43 | for ; i >= 0; i-- {
44 | if data[i] != 0x00 {
45 | break
46 | }
47 | }
48 | data = data[:i+1]
49 | return data
50 | }
51 |
52 | func DecodeBlob(blob [][]byte) []byte {
53 | var data []byte
54 | for _, b := range blob {
55 | data = append(data, b[0:31]...)
56 | }
57 |
58 | // XXX: the following removes trailing 0s, which could be unexpected for certain blobs
59 | i := len(data) - 1
60 | for ; i >= 0; i-- {
61 | if data[i] != 0x00 {
62 | break
63 | }
64 | }
65 | data = data[:i+1]
66 | return data
67 | }
68 |
--------------------------------------------------------------------------------
/tests/ctrl/ctrl.go:
--------------------------------------------------------------------------------
1 | package ctrl
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 | "os/exec"
8 | "strings"
9 | "sync"
10 | )
11 |
12 | func Run(cmd *exec.Cmd) error {
13 | cmd.Stdout = os.Stdout
14 | cmd.Stderr = os.Stderr
15 | if err := cmd.Start(); err != nil {
16 | log.Fatalf("cmd.Start: %v", err)
17 | }
18 | if err := cmd.Wait(); err != nil {
19 | if exiterr, ok := err.(*exec.ExitError); ok {
20 | return exiterr
21 | } else {
22 | log.Fatalf("cmd.Wait: %v", err)
23 | }
24 | }
25 | return nil
26 | }
27 |
28 | // guards against concurrent access to the docker daemon
29 | var dockerMutex sync.Mutex
30 |
31 | func StartServices(svcs ...string) error {
32 | dockerMutex.Lock()
33 | defer dockerMutex.Unlock()
34 |
35 | svcArg := strings.Join(svcs, " ")
36 | log.Printf("starting services %s", svcArg)
37 | err := Run(exec.Command("/bin/sh", "-c", fmt.Sprintf("docker compose up -d %s", svcArg)))
38 | if err != nil && err.(*exec.ExitError).ExitCode() == 127 {
39 | err = Run(exec.Command("/bin/sh", "-c", fmt.Sprintf("docker compose up -d %s", svcArg)))
40 | }
41 | return err
42 | }
43 |
44 | func StopService(svc string) error {
45 | err := Run(exec.Command("/bin/sh", "-c", fmt.Sprintf("docker compose stop %s", svc)))
46 | if err != nil && err.(*exec.ExitError).ExitCode() == 127 {
47 | err = Run(exec.Command("/bin/sh", "-c", fmt.Sprintf("docker compose stop %s", svc)))
48 | }
49 | return err
50 | }
51 |
52 | func StopDevnet() error {
53 | err := Run(exec.Command("/bin/sh", "-c", "docker compose down -v"))
54 | if err != nil && err.(*exec.ExitError).ExitCode() == 127 {
55 | err = Run(exec.Command("/bin/sh", "-c", "docker compose down -v"))
56 | }
57 | return err
58 | }
59 |
--------------------------------------------------------------------------------
/lighthouse/run_beacon_node.sh:
--------------------------------------------------------------------------------
1 | #!/bin/env bash
2 |
3 | set -Eeuo pipefail
4 |
5 | SUBSCRIBE_ALL_SUBNETS=
6 | DEBUG_LEVEL=${DEBUG_LEVEL:-debug}
7 | EXTERNAL_IP=$(ip addr show eth0 | grep inet | awk '{ print $2 }' | cut -d '/' -f1)
8 |
9 | # Get options
10 | while getopts "d:sh" flag; do
11 | case "${flag}" in
12 | d) DEBUG_LEVEL=${OPTARG};;
13 | s) SUBSCRIBE_ALL_SUBNETS="--subscribe-all-subnets";;
14 | h)
15 | echo "Start a beacon node"
16 | echo
17 | echo "usage: $0 "
18 | echo
19 | echo "Options:"
20 | echo " -s: pass --subscribe-all-subnets to 'lighthouse bn ...', default is not passed"
21 | echo " -d: DEBUG_LEVEL, default info"
22 | echo " -h: this help"
23 | echo
24 | echo "Positional arguments:"
25 | echo " DATADIR Value for --datadir parameter"
26 | echo " EXECUTION_ENDPOINT Value for --execution-endpoint parameter"
27 | exit
28 | ;;
29 | esac
30 | done
31 |
32 | set -x
33 |
34 | # Get positional arguments
35 | data_dir=${@:$OPTIND+0:1}
36 | execution_endpoint=${@:$OPTIND+1:1}
37 | network_port=$P2P_PORT
38 | http_port=8000
39 |
40 | exec lighthouse \
41 | --debug-level $DEBUG_LEVEL \
42 | bn \
43 | $SUBSCRIBE_ALL_SUBNETS \
44 | --datadir $data_dir \
45 | --testnet-dir /config_data/custom_config_data \
46 | --enable-private-discovery \
47 | --eth1 \
48 | --enr-address $EXTERNAL_IP \
49 | --enr-udp-port $network_port \
50 | --enr-tcp-port $network_port \
51 | --port $network_port \
52 | --http \
53 | --http-address 0.0.0.0 \
54 | --http-port $http_port \
55 | --disable-packet-filter \
56 | --target-peers 5 \
57 | --http-allow-sync-stalled \
58 | --execution-endpoint $execution_endpoint \
59 | --execution-jwt /config_data/cl/jwtsecret
60 |
--------------------------------------------------------------------------------
/erigon/erigon.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -exu -o pipefail
4 |
5 | ERIGON_DATA_DIR=/db
6 | ERIGON_KEYSTORE_DIR="$ERIGON_DATA_DIR/keystore"
7 | ERIGON_CHAINDATA_DIR="$ERIGON_DATA_DIR/erigon/chaindata"
8 | ERIGON_GENESIS=/config_data/custom_config_data/genesis.json
9 | NETWORK_ID=42424243
10 | BLOCK_SIGNER_PRIVATE_KEY="45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
11 | RPC_PORT=8545
12 | AUTH_PORT=8551
13 | WS_PORT=8546
14 | BOOTNODE_KEY_HEX=${BOOTNODE_KEY_HEX:-65f77f40c167b52b5cc70fb33582aecbdcd81062dc1438df00a3099a07079204}
15 | EXTERNAL_IP=$(ip addr show eth0 | grep inet | awk '{ print $2 }' | cut -d '/' -f1)
16 | ADDITIONAL_FLAGS="--nodiscover --nodekeyhex ${BOOTNODE_KEY_HEX} --nat extip:${EXTERNAL_IP}"
17 |
18 | mkdir -p ${ERIGON_DATA_DIR}
19 |
20 | # if [ ! -d "$ERIGON_KEYSTORE_DIR" ]; then
21 | # echo "$ERIGON_KEYSTORE_DIR missing, running account import"
22 | # touch ${ERIGON_DATA_DIR}/password
23 | # echo -n "$BLOCK_SIGNER_PRIVATE_KEY" | sed 's/0x//' > "$ERIGON_DATA_DIR"/block-signer-key
24 | # erigon account import \
25 | # --datadir="$ERIGON_DATA_DIR" \
26 | # --password="$ERIGON_DATA_DIR"/password \
27 | # "$ERIGON_DATA_DIR"/block-signer-key
28 | # else
29 | # echo "$ERIGON_KEYSTORE_DIR exists"
30 | # fi
31 |
32 | # init erigon data
33 | erigon init --datadir $ERIGON_DATA_DIR $ERIGON_GENESIS
34 |
35 | # TODO: figure out why beacon node doesn't advance when syncmode=snap
36 | exec erigon \
37 | --log.console.verbosity debug \
38 | --externalcl \
39 | --datadir "$ERIGON_DATA_DIR" \
40 | --networkid "$NETWORK_ID" \
41 | --nodiscover \
42 | --http \
43 | --http.api "eth,erigon,engine,debug,trace,txpool" \
44 | --http.corsdomain="*" \
45 | --http.vhosts="*" \
46 | --http.addr=0.0.0.0 \
47 | --http.port="$RPC_PORT" \
48 | --http.api=web3,debug,engine,eth,net,txpool \
49 | --authrpc.addr=0.0.0.0 \
50 | --authrpc.vhosts="*" \
51 | --authrpc.jwtsecret=/config_data/el/jwtsecret \
52 | --authrpc.port="$AUTH_PORT" \
53 | --prune=htc \
54 | --ws \
55 | ${ADDITIONAL_FLAGS}
56 |
--------------------------------------------------------------------------------
/lighthouse/setup.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | #
4 | # Deploys the deposit contract and makes deposits for $VALIDATOR_COUNT insecure deterministic validators.
5 | # Produces a testnet specification and a genesis state where the genesis time
6 | # is now + $GENESIS_DELAY.
7 | #
8 | # Generates datadirs for multiple validator keys according to the
9 | # $VALIDATOR_COUNT and $BN_COUNT variables.
10 | #
11 |
12 | set -o nounset -o errexit -o pipefail
13 |
14 | source ./vars.env
15 |
16 |
17 | NOW=`date +%s`
18 | GENESIS_TIME=`expr $NOW + $GENESIS_DELAY`
19 |
20 | lcli \
21 | new-testnet \
22 | --spec $SPEC_PRESET \
23 | --deposit-contract-address $DEPOSIT_CONTRACT_ADDRESS \
24 | --testnet-dir $TESTNET_DIR \
25 | --min-genesis-active-validator-count $GENESIS_VALIDATOR_COUNT \
26 | --min-genesis-time $GENESIS_TIME \
27 | --genesis-delay $GENESIS_DELAY \
28 | --genesis-fork-version $GENESIS_FORK_VERSION \
29 | --altair-fork-epoch $ALTAIR_FORK_EPOCH \
30 | --bellatrix-fork-epoch $BELLATRIX_FORK_EPOCH \
31 | --capella-fork-epoch $CAPELLA_FORK_EPOCH \
32 | --eip4844-fork-epoch $EIP4844_FORK_EPOCH \
33 | --ttd $TTD \
34 | --eth1-block-hash $ETH1_BLOCK_HASH \
35 | --eth1-id $CHAIN_ID \
36 | --eth1-follow-distance 1 \
37 | --seconds-per-slot $SECONDS_PER_SLOT \
38 | --seconds-per-eth1-block $SECONDS_PER_ETH1_BLOCK \
39 | --validator-count $GENESIS_VALIDATOR_COUNT \
40 | --interop-genesis-state \
41 | --force
42 |
43 | echo Specification and genesis.ssz generated at $TESTNET_DIR.
44 | echo "Generating $VALIDATOR_COUNT validators concurrently... (this may take a while)"
45 |
46 | lcli \
47 | insecure-validators \
48 | --count $VALIDATOR_COUNT \
49 | --base-dir $DATADIR \
50 | --node-count $BN_COUNT
51 |
52 | echo Validators generated with keystore passwords at $DATADIR.
53 |
54 | GENESIS_TIME=$(lcli pretty-ssz state_merge ${DATADIR}/testnet/genesis.ssz | jq | grep -Po 'genesis_time": "\K.*\d')
55 | CAPELLA_TIME=$((GENESIS_TIME + (CAPELLA_FORK_EPOCH * 32 * SECONDS_PER_SLOT)))
56 | EIP4844_TIME=$((GENESIS_TIME + (EIP4844_FORK_EPOCH * 32 * SECONDS_PER_SLOT)))
57 |
58 | sed -i 's/"shanghaiTime".*$/"shanghaiTime": '"$CAPELLA_TIME"',/g' genesis.json
59 | sed -i 's/"shardingForkTime".*$/"shardingForkTime": '"$EIP4844_TIME"',/g' genesis.json
60 |
--------------------------------------------------------------------------------
/geth/geth.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -exu -o pipefail
4 |
5 | VERBOSITY=${GETH_VERBOSITY:-4}
6 | GETH_DATA_DIR=/db
7 | GETH_KEYSTORE_DIR="$GETH_DATA_DIR/keystore"
8 | GETH_CHAINDATA_DIR="$GETH_DATA_DIR/geth/chaindata"
9 | GETH_GENESIS=/config_data/custom_config_data/genesis.json
10 | NETWORK_ID=42424243
11 | BLOCK_SIGNER_PRIVATE_KEY="45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
12 | RPC_PORT=8545
13 | AUTH_PORT=8551
14 | WS_PORT=8546
15 | BOOTNODE_KEY_HEX=${BOOTNODE_KEY_HEX:-65f77f40c167b52b5cc70fb33582aecbdcd81062dc1438df00a3099a07079204}
16 | EXTERNAL_IP=$(ip addr show eth0 | grep inet | awk '{ print $2 }' | cut -d '/' -f1)
17 | ADDITIONAL_FLAGS="--nodiscover --nodekeyhex ${BOOTNODE_KEY_HEX} --nat extip:${EXTERNAL_IP}"
18 |
19 | mkdir -p ${GETH_DATA_DIR}
20 |
21 | if [ ! -d "$GETH_KEYSTORE_DIR" ]; then
22 | echo "$GETH_KEYSTORE_DIR missing, running account import"
23 | touch ${GETH_DATA_DIR}/password
24 | echo -n "$BLOCK_SIGNER_PRIVATE_KEY" | sed 's/0x//' > "$GETH_DATA_DIR"/block-signer-key
25 | geth account import \
26 | --datadir="$GETH_DATA_DIR" \
27 | --password="$GETH_DATA_DIR"/password \
28 | "$GETH_DATA_DIR"/block-signer-key
29 | else
30 | echo "$GETH_KEYSTORE_DIR exists"
31 | fi
32 |
33 | # init geth data
34 | geth init --datadir $GETH_DATA_DIR $GETH_GENESIS
35 |
36 | # TODO: figure out why beacon node doesn't advance when syncmode=snap
37 | exec geth \
38 | --datadir "$GETH_DATA_DIR" \
39 | --verbosity "$VERBOSITY" \
40 | --networkid "$NETWORK_ID" \
41 | --nodiscover \
42 | --http \
43 | --http.corsdomain="*" \
44 | --http.vhosts="*" \
45 | --http.addr=0.0.0.0 \
46 | --http.port="$RPC_PORT" \
47 | --http.api=web3,debug,engine,eth,net,txpool \
48 | --authrpc.addr=0.0.0.0 \
49 | --authrpc.vhosts="*" \
50 | --authrpc.jwtsecret=/config_data/el/jwtsecret \
51 | --authrpc.port="$AUTH_PORT" \
52 | --ws \
53 | --ws.addr=0.0.0.0 \
54 | --ws.port="$WS_PORT" \
55 | --ws.origins="*" \
56 | --ws.api=debug,eth,txpool,net,engine \
57 | ${ADDITIONAL_FLAGS} \
58 | --allow-insecure-unlock \
59 | --password "${GETH_DATA_DIR}/password" \
60 | --syncmode=full \
61 | --unlock "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" \
62 | --mine
63 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | SERVICES=geth-1\
2 | geth-2\
3 | besu-1 \
4 | prysm-beacon-node\
5 | prysm-beacon-node-follower\
6 | prysm-validator-node\
7 | lighthouse-beacon-node\
8 | lighthouse-beacon-node-follower\
9 | lighthouse-validator-node\
10 | jaeger-tracing
11 |
12 | devnet-setup: devnet-clean
13 | docker compose --project-name eip4844-interop up genesis-generator
14 |
15 | devnet-build:
16 | docker compose --project-name eip4844-interop build ${SERVICES}
17 |
18 | # First build then setup so we don't start after the genesis_delay
19 | devnet-up: devnet-build devnet-setup
20 | docker compose --project-name eip4844-interop up -d ${SERVICES}
21 |
22 | lighthouse-up: devnet-build devnet-setup
23 | docker compose --project-name eip4844-interop up -d --build\
24 | geth-1\
25 | geth-2\
26 | lighthouse-beacon-node\
27 | lighthouse-beacon-node-follower\
28 | lighthouse-validator-node
29 |
30 | lodestar-up:
31 | docker compose --project-name eip4844-interop up -d\
32 | geth-1\
33 | geth-2\
34 | lodestar-beacon-node\
35 | lodestar-beacon-node-follower\
36 |
37 | lighthouse-prysm: devnet-setup
38 | docker compose --project-name eip4844-interop up -d --build lighthouse-validator-node
39 | sleep 300
40 | docker compose --project-name eip4844-interop up -d --build prysm-beacon-node-follower
41 |
42 | besu-prysm-up: devnet-build devnet-setup
43 | docker compose --project-name eip4844-interop up -d --build \
44 | besu-1 \
45 | prysm-beacon-node-besu-el \
46 | prysm-beacon-node-follower \
47 | prysm-validator-node-besu-el
48 |
49 |
50 | devnet-down:
51 | docker compose --project-name eip4844-interop down -v
52 |
53 | devnet-restart: devnet-down devnet-up
54 |
55 | devnet-clean:
56 | docker compose --project-name eip4844-interop down --rmi local --volumes
57 | docker image ls 'eip4844-interop*' --format='{{.Repository}}' | xargs -r docker rmi
58 | docker volume ls --filter name=eip4844-interop --format='{{.Name}}' | xargs -r docker volume rm
59 |
60 | blobtx-test: devnet-setup
61 | go run ./tests/blobtx $(el)
62 |
63 | pre4844-test: devnet-setup
64 | go run ./tests/pre-4844 $(el)
65 |
66 | initial-sync-test: devnet-setup
67 | go run ./tests/initial-sync $(el)
68 |
69 | fee-market-test: devnet-setup
70 | go run ./tests/fee-market $(el)
71 |
72 | .PHONY: devnet-clean
73 |
--------------------------------------------------------------------------------
/shared/config.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 | "io/ioutil"
8 | "log"
9 | "net/http"
10 | "os"
11 | "strings"
12 |
13 | "github.com/ethereum/go-ethereum/params"
14 | )
15 |
16 | func GetBaseDir() string {
17 | path := os.Getenv("TEST_INTEROP_BASEDIR")
18 | if path == "" {
19 | var err error
20 | path, err = os.Getwd()
21 | if err != nil {
22 | log.Printf("error geting interop basedir (%v). defaulting to /", err)
23 | }
24 | }
25 | return path
26 | }
27 |
28 | func GethChainConfigFilepath() string {
29 | return fmt.Sprintf("%s/shared/generated-configs/custom_config_data/genesis.json", GetBaseDir())
30 | }
31 |
32 | func BeaconChainConfigFilepath() string {
33 | return fmt.Sprintf("%s/shared/generated-configs/custom_config_data/config.yaml", GetBaseDir())
34 | }
35 |
36 | func UpdateChainConfig(config *params.ChainConfig) error {
37 | file, err := json.MarshalIndent(config, "", " ")
38 | if err != nil {
39 | return err
40 | }
41 | path := GethChainConfigFilepath()
42 | return ioutil.WriteFile(path, file, 0644)
43 | }
44 |
45 | func GetBeaconMultiAddress() (string, error) {
46 | return getMultiaddress("http://" + BeaconAPI)
47 | }
48 |
49 | func GetBeaconFollowerMultiAddress() (string, error) {
50 | return getMultiaddress("http://" + BeaconFollowerAPI)
51 | }
52 |
53 | func getMultiaddress(beaconAPI string) (string, error) {
54 | url := fmt.Sprintf("%s/eth/v1/node/identity", beaconAPI)
55 | r, err := http.Get(url)
56 | if err != nil {
57 | return "", err
58 | }
59 | defer r.Body.Close()
60 |
61 | type Data struct {
62 | Data struct {
63 | P2PAddresses []string `json:"p2p_addresses"`
64 | } `json:"data"`
65 | }
66 | var data Data
67 | if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
68 | return "", err
69 | }
70 | if len(data.Data.P2PAddresses) == 0 {
71 | return "", errors.New("no multiaddresses found")
72 | }
73 | for _, a := range data.Data.P2PAddresses {
74 | // prefer localhost address as the others are inaccessible outside the docker container
75 | if strings.HasPrefix(a, "/ip4/127.0.0.1") {
76 | return a, nil
77 | }
78 | }
79 | return data.Data.P2PAddresses[0], nil
80 | }
81 |
82 | var (
83 | GethRPC = "http://localhost:8545"
84 | PrivateKey = "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
85 | BeaconAPI = "localhost:8000"
86 | BeaconFollowerAPI = "localhost:8001"
87 | //ValidatorAPI = "http://localhost:7500"
88 | )
89 |
--------------------------------------------------------------------------------
/shared/genesis-generator-configs/cl/config.yaml:
--------------------------------------------------------------------------------
1 | # Extends the mainnet preset
2 | PRESET_BASE: 'mainnet'
3 | CONFIG_NAME: testnet # needs to exist because of Prysm. Otherwise it conflicts with mainnet genesis
4 |
5 | # Genesis
6 | # ---------------------------------------------------------------
7 | # `2**14` (= 16,384)
8 | MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: $NUMBER_OF_VALIDATORS
9 | # Mar-01-2021 08:53:32 AM +UTC
10 | # This is an invalid valid and should be updated when you create the genesis
11 | MIN_GENESIS_TIME: $CL_TIMESTAMP
12 | GENESIS_FORK_VERSION: $GENESIS_FORK_VERSION
13 | GENESIS_DELAY: $GENESIS_DELAY
14 |
15 |
16 | # Forking
17 | # ---------------------------------------------------------------
18 | # Some forks are disabled for now:
19 | # - These may be re-assigned to another fork-version later
20 | # - Temporarily set to max uint64 value: 2**64 - 1
21 |
22 | # Altair
23 | ALTAIR_FORK_VERSION: $ALTAIR_FORK_VERSION
24 | ALTAIR_FORK_EPOCH: 0
25 | # Merge
26 | BELLATRIX_FORK_VERSION: $BELLATRIX_FORK_VERSION
27 | BELLATRIX_FORK_EPOCH: 0
28 | TERMINAL_TOTAL_DIFFICULTY: 0
29 | TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000
30 | TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615
31 |
32 | # Capella
33 | CAPELLA_FORK_VERSION: $CAPELLA_FORK_VERSION
34 | CAPELLA_FORK_EPOCH: $CAPELLA_FORK_EPOCH
35 |
36 | # EIP4844
37 | EIP4844_FORK_VERSION: $EIP4844_FORK_VERSION
38 | EIP4844_FORK_EPOCH: $EIP4844_FORK_EPOCH
39 |
40 | # Time parameters
41 | # ---------------------------------------------------------------
42 | # 12 seconds
43 | SECONDS_PER_SLOT: $SECONDS_PER_SLOT
44 | SLOTS_PER_EPOCH: $SLOTS_PER_EPOCH
45 | # 14 (estimate from Eth1 mainnet)
46 | SECONDS_PER_ETH1_BLOCK: 12
47 | # 2**0 (= 1) epochs ~1 hours
48 | MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 1
49 | # 2**8 (= 256) epochs ~27 hours
50 | SHARD_COMMITTEE_PERIOD: 1
51 | # 2**11 (= 2,048) Eth1 blocks ~8 hours
52 | ETH1_FOLLOW_DISTANCE: 12
53 |
54 |
55 | # Validator cycle
56 | # ---------------------------------------------------------------
57 | # 2**2 (= 4)
58 | INACTIVITY_SCORE_BIAS: 4
59 | # 2**4 (= 16)
60 | INACTIVITY_SCORE_RECOVERY_RATE: 16
61 | # 2**4 * 10**9 (= 16,000,000,000) Gwei
62 | EJECTION_BALANCE: 31000000000
63 | # 2**2 (= 4)
64 | MIN_PER_EPOCH_CHURN_LIMIT: 4
65 | # 2**16 (= 65,536)
66 | CHURN_LIMIT_QUOTIENT: 65536
67 |
68 | # Fork choice
69 | # ---------------------------------------------------------------
70 | # 40%
71 | PROPOSER_SCORE_BOOST: 40
72 |
73 | # Deposit contract
74 | # ---------------------------------------------------------------
75 | DEPOSIT_CHAIN_ID: $CHAIN_ID
76 | DEPOSIT_NETWORK_ID: $CHAIN_ID
77 | DEPOSIT_CONTRACT_ADDRESS: $DEPOSIT_CONTRACT_ADDRESS
78 |
--------------------------------------------------------------------------------
/lighthouse/generate-genesis.sh:
--------------------------------------------------------------------------------
1 | #!/bin/env bash
2 |
3 | # Deploys the deposit contract and makes deposits for $VALIDATOR_COUNT insecure deterministic validators.
4 | # Produces a testnet specification and a genesis state where the genesis time
5 | # is now + $GENESIS_DELAY.
6 | #
7 | # Generates datadirs for multiple validator keys according to the
8 | # $VALIDATOR_COUNT and $BN_COUNT variables.
9 | #
10 |
11 | set -o nounset -o errexit -o pipefail
12 |
13 | source /config/vars.env
14 |
15 | if [ ! -z "$(ls -A $TESTNET_DIR)" ]; then
16 | echo "testnet directory already exists. exiting"
17 | exit 0
18 | fi
19 |
20 | NOW=`date +%s`
21 | GENESIS_TIME=`expr $NOW + $GENESIS_DELAY`
22 |
23 | lcli \
24 | new-testnet \
25 | --spec $SPEC_PRESET \
26 | --deposit-contract-address $DEPOSIT_CONTRACT_ADDRESS \
27 | --testnet-dir $TESTNET_DIR \
28 | --min-genesis-active-validator-count $GENESIS_VALIDATOR_COUNT \
29 | --min-genesis-time $GENESIS_TIME \
30 | --genesis-delay $GENESIS_DELAY \
31 | --genesis-fork-version $GENESIS_FORK_VERSION \
32 | --altair-fork-epoch $ALTAIR_FORK_EPOCH \
33 | --bellatrix-fork-epoch $BELLATRIX_FORK_EPOCH \
34 | --capella-fork-epoch $CAPELLA_FORK_EPOCH \
35 | --eip4844-fork-epoch $EIP4844_FORK_EPOCH \
36 | --ttd $TTD \
37 | --eth1-block-hash $ETH1_BLOCK_HASH \
38 | --eth1-id $CHAIN_ID \
39 | --eth1-follow-distance 1 \
40 | --seconds-per-slot $SECONDS_PER_SLOT \
41 | --seconds-per-eth1-block $SECONDS_PER_ETH1_BLOCK \
42 | --validator-count $GENESIS_VALIDATOR_COUNT \
43 | --interop-genesis-state \
44 | --force
45 |
46 | echo Specification and genesis.ssz generated at $TESTNET_DIR.
47 | echo "Generating $VALIDATOR_COUNT validators concurrently... (this may take a while)"
48 |
49 | lcli \
50 | insecure-validators \
51 | --count $VALIDATOR_COUNT \
52 | --base-dir $DATADIR \
53 | --testnet-dir $TESTNET_DIR \
54 | --node-count 1
55 |
56 | echo Validators generated with keystore passwords at $DATADIR.
57 |
58 | GENESIS_TIME=$(lcli pretty-ssz state_merge $TESTNET_DIR/genesis.ssz | jq | grep -Po 'genesis_time": "\K.*\d')
59 | CAPELLA_TIME=$((GENESIS_TIME + (CAPELLA_FORK_EPOCH * 32 * SECONDS_PER_SLOT)))
60 | EIP4844_TIME=$((GENESIS_TIME + (EIP4844_FORK_EPOCH * 32 * SECONDS_PER_SLOT)))
61 |
62 | cp /config/genesis.json $TESTNET_DIR/genesis.json
63 |
64 | sed -i 's/"shanghaiTime".*$/"shanghaiTime": '"$CAPELLA_TIME"',/g' $TESTNET_DIR/genesis.json
65 | sed -i 's/"shardingForkTime".*$/"shardingForkTime": '"$EIP4844_TIME"',/g' $TESTNET_DIR/genesis.json
66 |
67 | cp $TESTNET_DIR/genesis.json /config/generated-genesis.json
68 |
69 | # we need to edit first before copying the file to the bind-mount. Redirects do not work here
70 | cp $TESTNET_DIR/config.yaml /tmp/config.yaml
71 | # unquote strings for easier compatibilty with yaml parsers
72 | sed -i 's/"//g' /tmp/config.yaml
73 | cp /tmp/config.yaml /config/generated-config.yaml
74 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Setup
2 |
3 | 1. Clone this repository
4 | 2. Run `git submodule update --init`
5 |
6 | Everytime you need to test a change in prysm or geth, run `git submodule update --remote`
7 |
8 | ## Running the Devnet
9 | 1. (_Optional_) Run `make devnet-clean` to start from a clean slate
10 | 2. Run `make devnet-up`
11 | 3. Visit to visualize beacon and validator node traces
12 |
13 | ## How to run tests
14 |
15 | ### For prysm + geth combination
16 |
17 | 1. `make devnet-clean` to clean old containers
18 | 2. `make blobtx-test el=prysm` to run the test, checkout Makefile to find other tests to run
19 |
20 | ## Adding new Clients
21 | Interop uses [ethereum-genesis-generator](https://github.com/inphi/ethereum-genesis-generator/tree/eip4844) to generate the configuration.
22 | New clients can be added by create a docker compose service running the client. Recommend taking a look at the existing docker compose services to get an idea.
23 |
24 | The `genesis-generator` docker service creates the genesis configuration your client will need to run a local testnet. The configs live in the `config_data` volume with the following layout:
25 |
26 | ```
27 | /config_data
28 | ├── cl
29 | │ └── jwtsecret
30 | ├── custom_config_data
31 | │ ├── besu.json
32 | │ ├── boot_enr.txt
33 | │ ├── boot_enr.yaml
34 | │ ├── bootstrap_nodes.txt
35 | │ ├── chainspec.json
36 | │ ├── config.yaml
37 | │ ├── deploy_block.txt
38 | │ ├── deposit_contract.txt
39 | │ ├── deposit_contract_block.txt
40 | │ ├── deposit_contract_block_hash.txt
41 | │ ├── genesis.json
42 | │ ├── genesis.ssz
43 | │ ├── mnemonics.yaml
44 | │ ├── parsedBeaconState.json
45 | │ └── tranches
46 | │ └── tranche_0000.txt
47 | └── el
48 | └── jwtsecret
49 | ```
50 | The generated CL configs contain the following noteworthy settings:
51 | - `GENESIS_TIMESTAMP`: this is set to the current time
52 | - `GENESIS_DELAY`: this is set to 60 seconds, giving clients a minute to build and run their nodes before genesis begins.
53 | - `SECONDS_PER_SLOT`: set to `3` to shorten test iteration.
54 |
55 | ### Bootnode service
56 | The `bootnode` docker service can be used by consensus clients to bootstrap their node. The ENR of the bootnode can be found at `/config_data/custom_config_data/boot_enr.yaml`.
57 |
58 | ### Peering a new client with devnet
59 | Once you've configured your client for interop, you can test it by connecting it with an EL client (like `geth-2`), the peering it with a known working validator (ex: `prysm-validator-node`). For example, to peer a hypothetically added teku-node:
60 | ```
61 | docker compose run genesis-generator && \
62 | docker compose run prysm-validator-node teku-node -d
63 | ```
64 |
65 | Once EIP4844 epoch has occurred, you can try sending a blob transaction locally to confirm that the blobs are sidecar'd to the beacon chain. This can be done with the following script:
66 | ```
67 | go run ./upload ./eth.png
68 | ```
69 |
--------------------------------------------------------------------------------
/tests/pre-4844/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "log"
6 | "math/big"
7 | "os"
8 | "time"
9 |
10 | "github.com/Inphi/eip4844-interop/shared"
11 | "github.com/Inphi/eip4844-interop/tests/ctrl"
12 | "github.com/ethereum/go-ethereum"
13 | "github.com/ethereum/go-ethereum/common"
14 | "github.com/ethereum/go-ethereum/core/types"
15 | "github.com/ethereum/go-ethereum/crypto"
16 | "github.com/ethereum/go-ethereum/params"
17 | )
18 |
19 | // Asserts that transaction still work before the 4844 fork in execution
20 | func main() {
21 | clientName := "prysm"
22 | if len(os.Args) > 1 {
23 | clientName = os.Args[1]
24 | }
25 |
26 | ctrl.InitE2ETest(clientName)
27 |
28 | env := ctrl.GetEnv()
29 | chainId := env.GethChainConfig.ChainID
30 | signer := types.NewDankSigner(chainId)
31 |
32 | ctx, cancel := context.WithTimeout(context.Background(), time.Minute*20)
33 | defer cancel()
34 |
35 | client, err := ctrl.GetExecutionClient(ctx)
36 | if err != nil {
37 | log.Fatalf("unable to get execution client: %v", err)
38 | }
39 |
40 | key, err := crypto.HexToECDSA(shared.PrivateKey)
41 | if err != nil {
42 | log.Fatalf("Failed to load private key: %v", err)
43 | }
44 |
45 | nonce, err := client.PendingNonceAt(ctx, crypto.PubkeyToAddress(key.PublicKey))
46 | if err != nil {
47 | log.Fatalf("Error getting nonce: %v", err)
48 | }
49 | log.Printf("Nonce: %d", nonce)
50 |
51 | gasTipCap, err := client.SuggestGasTipCap(ctx)
52 | if err != nil {
53 | log.Fatalf("Suggest gas tip cap: %v", err)
54 | }
55 | gasFeeCap, err := client.SuggestGasPrice(ctx)
56 | if err != nil {
57 | log.Fatalf("Suggest gas fee price: %v", err)
58 | }
59 |
60 | to := common.HexToAddress("ffb38a7a99e3e2335be83fc74b7faa19d5531243")
61 | tx, err := types.SignTx(types.NewTx(&types.DynamicFeeTx{
62 | Nonce: nonce,
63 | To: &to,
64 | Value: big.NewInt(10000),
65 | Gas: params.TxGas,
66 | GasTipCap: gasTipCap,
67 | GasFeeCap: gasFeeCap,
68 | }), signer, key)
69 | if err != nil {
70 | log.Fatalf("Error signing tx: %v", err)
71 | }
72 |
73 | err = client.SendTransaction(ctx, tx)
74 | if err != nil {
75 | log.Fatalf("Error sending tx: %v", err)
76 | }
77 |
78 | log.Printf("Waiting for transaction (%v) to be included...", tx.Hash())
79 | var receipt *types.Receipt
80 | for {
81 | receipt, err = client.TransactionReceipt(ctx, tx.Hash())
82 | if err == ethereum.NotFound {
83 | time.Sleep(time.Second * 1)
84 | continue
85 | }
86 | if err != nil {
87 | log.Fatalf("Error getting tx receipt for %v: %v", tx.Hash(), err)
88 | }
89 | break
90 | }
91 |
92 | blockHash := receipt.BlockHash.Hex()
93 | blk, err := client.BlockByHash(ctx, common.HexToHash(blockHash))
94 | if err != nil {
95 | log.Fatalf("Error getting block: %v", err)
96 | }
97 |
98 | shardingForkTime := ctrl.GetEnv().GethChainConfig.ShardingForkTime
99 | if shardingForkTime == nil {
100 | log.Fatalf("shardingForkTime is not set in configuration")
101 | }
102 | eip4844ForkTime := *shardingForkTime
103 | if blk.Time() > eip4844ForkTime {
104 | // TODO: Avoid this issue by configuring the chain config at runtime
105 | log.Fatalf("Test condition violation. Transaction must be included before eip4844 fork. Check the geth chain config")
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/tests/ctrl/services.go:
--------------------------------------------------------------------------------
1 | package ctrl
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "net/http"
8 | "time"
9 |
10 | "github.com/Inphi/eip4844-interop/shared"
11 | "github.com/ethereum/go-ethereum/ethclient"
12 | "github.com/prysmaticlabs/prysm/v3/api/client/beacon"
13 | )
14 |
15 | type Service interface {
16 | Start(ctx context.Context) error
17 | Stop(ctx context.Context) error
18 | Started() <-chan struct{}
19 | }
20 |
21 | func NewBeaconNode(clientName string) Service {
22 | url := fmt.Sprintf("http://%s/eth/v1/beacon/genesis", shared.BeaconAPI)
23 | return newDockerService(fmt.Sprintf("%s-beacon-node", clientName), url)
24 | }
25 |
26 | func NewValidatorNode(clientName string) Service {
27 | return newDockerService(fmt.Sprintf("%s-validator-node", clientName), "")
28 | }
29 |
30 | func NewBeaconNodeFollower(clientName string) Service {
31 | url := fmt.Sprintf("http://%s/eth/v1/beacon/genesis", shared.BeaconFollowerAPI)
32 | return newDockerService(fmt.Sprintf("%s-beacon-node-follower", clientName), url)
33 | }
34 |
35 | func GetBeaconNodeClient(ctx context.Context) (*beacon.Client, error) {
36 | client, err := beacon.NewClient(shared.BeaconAPI)
37 | if err != nil {
38 | return nil, fmt.Errorf("%w: failed to create beacon API client", err)
39 | }
40 | return client, nil
41 | }
42 |
43 | func GetBeaconNodeFollowerClient(ctx context.Context) (*beacon.Client, error) {
44 | client, err := beacon.NewClient(shared.BeaconFollowerAPI)
45 | if err != nil {
46 | return nil, fmt.Errorf("%w: failed to create beacon follower API client", err)
47 | }
48 | return client, nil
49 | }
50 |
51 | func NewGethNode() Service {
52 | return newDockerService("geth-1", shared.GethRPC)
53 | }
54 |
55 | func NewGethNode2() Service {
56 | return newDockerService("geth-2", shared.GethRPC)
57 | }
58 |
59 | func GetExecutionClient(ctx context.Context) (*ethclient.Client, error) {
60 | client, err := ethclient.DialContext(ctx, shared.GethRPC)
61 | if err != nil {
62 | return nil, fmt.Errorf("%w: Failed to connect to the Ethereum client", err)
63 | }
64 | return client, nil
65 | }
66 |
67 | type dockerService struct {
68 | started chan struct{}
69 | svcname string
70 | statusURL string
71 | }
72 |
73 | func (s *dockerService) Start(ctx context.Context) error {
74 | if err := StartServices(s.svcname); err != nil {
75 | return err
76 | }
77 | if s.statusURL == "" {
78 | return nil
79 | }
80 | req, err := http.NewRequestWithContext(ctx, "GET", s.statusURL, nil)
81 | if err != nil {
82 | return err
83 | }
84 | // loop until the status request returns successfully
85 | for {
86 | if _, err := http.DefaultClient.Do(req); err == nil {
87 | close(s.started)
88 | return nil
89 | }
90 | select {
91 | case <-ctx.Done():
92 | return ctx.Err()
93 | case <-time.After(1 * time.Second):
94 | log.Printf("%s: waiting for a successful status check at %s", s.svcname, s.statusURL)
95 | }
96 | }
97 | }
98 |
99 | func (s *dockerService) Stop(ctx context.Context) error {
100 | return StopService(s.svcname)
101 | }
102 |
103 | func (s *dockerService) Started() <-chan struct{} {
104 | return s.started
105 | }
106 |
107 | func ServiceReady(ctx context.Context, svc Service) error {
108 | for {
109 | select {
110 | case <-ctx.Done():
111 | return ctx.Err()
112 | case <-svc.Started():
113 | return nil
114 | }
115 | }
116 | }
117 |
118 | func newDockerService(svcname string, statusURL string) Service {
119 | return &dockerService{
120 | started: make(chan struct{}),
121 | svcname: svcname,
122 | statusURL: statusURL,
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/upload/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "log"
7 | "math/big"
8 | "os"
9 | "time"
10 |
11 | "github.com/Inphi/eip4844-interop/shared"
12 | "github.com/ethereum/go-ethereum/common"
13 | "github.com/ethereum/go-ethereum/core/types"
14 | "github.com/ethereum/go-ethereum/crypto"
15 | "github.com/ethereum/go-ethereum/ethclient"
16 | "github.com/holiman/uint256"
17 | "github.com/protolambda/ztyp/view"
18 | )
19 |
20 | func main() {
21 | prv := "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
22 |
23 | before := flag.Uint64("before", 0, "Block to wait for before submitting transaction")
24 | after := flag.Uint64("after", 0, "Block to wait for after submitting transaction")
25 | addr := flag.String("addr", "http://localhost:8545", "JSON-RPC endpoint")
26 | flag.Parse()
27 |
28 | file := flag.Arg(0)
29 | if file == "" {
30 | log.Fatalf("File parameter missing")
31 | }
32 | data, err := os.ReadFile(file)
33 | if err != nil {
34 | log.Fatalf("Error reading file: %v", err)
35 | }
36 |
37 | ctx := context.Background()
38 | client, err := ethclient.DialContext(ctx, *addr)
39 | if err != nil {
40 | log.Fatalf("Failed to connect to the Ethereum client: %v", err)
41 | }
42 |
43 | chainId, err := client.ChainID(ctx)
44 | if err != nil {
45 | log.Fatalf("failed to retrieve chain id: %v", err)
46 | }
47 | signer := types.NewDankSigner(chainId)
48 |
49 | key, err := crypto.HexToECDSA(prv)
50 | if err != nil {
51 | log.Fatalf("Failed to load private key: %v", err)
52 | }
53 |
54 | if *before > 0 {
55 | waitForBlock(ctx, client, *before)
56 | }
57 |
58 | bn, err := client.BlockNumber(ctx)
59 | if err != nil {
60 | log.Fatalf("Error getting block number: %v", err)
61 | }
62 | // note: etherumjs doesn't support PendingNonceAt
63 | nonce, err := client.NonceAt(ctx, crypto.PubkeyToAddress(key.PublicKey), new(big.Int).SetUint64(bn))
64 | if err != nil {
65 | log.Fatalf("Error getting nonce: %v", err)
66 | }
67 | log.Printf("Nonce: %d", nonce)
68 |
69 | blobs := shared.EncodeBlobs(data)
70 | commitments, versionedHashes, aggregatedProof, err := blobs.ComputeCommitmentsAndAggregatedProof()
71 |
72 | to := common.HexToAddress("ffb38a7a99e3e2335be83fc74b7faa19d5531243")
73 | txData := types.SignedBlobTx{
74 | Message: types.BlobTxMessage{
75 | ChainID: view.Uint256View(*uint256.NewInt(chainId.Uint64())),
76 | Nonce: view.Uint64View(nonce),
77 | Gas: 210000,
78 | GasFeeCap: view.Uint256View(*uint256.NewInt(5000000000)),
79 | GasTipCap: view.Uint256View(*uint256.NewInt(5000000000)),
80 | MaxFeePerDataGas: view.Uint256View(*uint256.NewInt(3000000000)), // needs to be at least the min fee
81 | Value: view.Uint256View(*uint256.NewInt(12345678)),
82 | To: types.AddressOptionalSSZ{Address: (*types.AddressSSZ)(&to)},
83 | BlobVersionedHashes: versionedHashes,
84 | },
85 | }
86 |
87 | wrapData := types.BlobTxWrapData{
88 | BlobKzgs: commitments,
89 | Blobs: blobs,
90 | KzgAggregatedProof: aggregatedProof,
91 | }
92 | tx := types.NewTx(&txData, types.WithTxWrapData(&wrapData))
93 | tx, err = types.SignTx(tx, signer, key)
94 | if err != nil {
95 | log.Fatalf("Error signing tx: %v", err)
96 | }
97 |
98 | err = client.SendTransaction(ctx, tx)
99 | if err != nil {
100 | log.Fatalf("Error sending tx: %v", err)
101 | }
102 |
103 | log.Printf("Transaction submitted. hash=%v", tx.Hash())
104 |
105 | if *after > 0 {
106 | waitForBlock(ctx, client, *after)
107 | }
108 | }
109 |
110 | func waitForBlock(ctx context.Context, client *ethclient.Client, block uint64) {
111 | for {
112 | bn, err := client.BlockNumber(ctx)
113 | if err != nil {
114 | log.Fatalf("Error requesting block number: %v", err)
115 | }
116 | if bn >= block {
117 | return
118 | }
119 | log.Printf("Waiting for block %d, current %d", block, bn)
120 | time.Sleep(1 * time.Second)
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/tests/blobtx/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "log"
6 | "math/big"
7 | "os"
8 | "time"
9 |
10 | "github.com/Inphi/eip4844-interop/shared"
11 | "github.com/Inphi/eip4844-interop/tests/ctrl"
12 | "github.com/Inphi/eip4844-interop/tests/util"
13 | "github.com/ethereum/go-ethereum/common"
14 | "github.com/ethereum/go-ethereum/core/types"
15 | "github.com/ethereum/go-ethereum/crypto"
16 | "github.com/ethereum/go-ethereum/ethclient"
17 | "github.com/holiman/uint256"
18 | "github.com/protolambda/ztyp/view"
19 | )
20 |
21 | func GetBlobs() types.Blobs {
22 | // dummy data for the test
23 | return shared.EncodeBlobs([]byte("EKANS"))
24 | }
25 |
26 | // 1. Uploads blobs
27 | // 2. Downloads blobs
28 | // 3. Asserts that downloaded blobs match the upload
29 | // 4. Asserts execution and beacon block attributes
30 | func main() {
31 | clientName := "prysm"
32 | if len(os.Args) > 1 {
33 | clientName = os.Args[1]
34 | }
35 | ctrl.InitE2ETest(clientName)
36 | ctrl.WaitForShardingFork()
37 | ctrl.WaitForEip4844ForkEpoch()
38 | env := ctrl.GetEnv()
39 |
40 | ctx, cancel := context.WithTimeout(context.Background(), time.Minute*20)
41 | defer cancel()
42 |
43 | ethClient, err := ctrl.GetExecutionClient(ctx)
44 | if err != nil {
45 | log.Fatalf("unable to get execution client: %v", err)
46 | }
47 | beaconClient, err := ctrl.GetBeaconNodeClient(ctx)
48 | if err != nil {
49 | log.Fatalf("unable to get beacon client: %v", err)
50 | }
51 |
52 | blobs := GetBlobs()
53 |
54 | // Retrieve the current slot to being our blobs search on the beacon chain
55 | startSlot := util.GetHeadSlot(ctx, beaconClient)
56 |
57 | chainID := env.GethChainConfig.ChainID
58 | UploadBlobs(ctx, ethClient, chainID, blobs)
59 | util.WaitForNextSlots(ctx, beaconClient, 1)
60 | slot := util.FindBlobSlot(ctx, beaconClient, startSlot)
61 |
62 | multiaddr, err := shared.GetBeaconMultiAddress()
63 | if err != nil {
64 | log.Fatalf("unable to get beacon multiaddr: %v", err)
65 | }
66 | followerMultiaddr, err := shared.GetBeaconFollowerMultiAddress()
67 | if err != nil {
68 | log.Fatalf("unable to get beacon multiaddr: %v", err)
69 | }
70 |
71 | log.Printf("checking blob from beacon node")
72 | downloadedData := util.DownloadBlobs(ctx, slot, 1, multiaddr)
73 | downloadedBlobs := shared.EncodeBlobs(downloadedData)
74 | util.AssertBlobsEquals(blobs, downloadedBlobs)
75 |
76 | log.Printf("checking blob from beacon node follower")
77 | sleep := time.Second * 2 * time.Duration(env.BeaconChainConfig.SecondsPerSlot)
78 | log.Printf("wait a bit to sync: %v", sleep)
79 | time.Sleep(sleep) // wait a bit for sync
80 | downloadedData = util.DownloadBlobs(ctx, slot, 1, followerMultiaddr)
81 | downloadedBlobs = shared.EncodeBlobs(downloadedData)
82 | util.AssertBlobsEquals(blobs, downloadedBlobs)
83 | }
84 |
85 | func UploadBlobs(ctx context.Context, client *ethclient.Client, chainID *big.Int, blobs types.Blobs) {
86 | signer := types.NewDankSigner(chainID)
87 |
88 | key, err := crypto.HexToECDSA(shared.PrivateKey)
89 | if err != nil {
90 | log.Fatalf("Failed to load private key: %v", err)
91 | }
92 |
93 | nonce, err := client.PendingNonceAt(ctx, crypto.PubkeyToAddress(key.PublicKey))
94 | if err != nil {
95 | log.Fatalf("Error getting nonce: %v", err)
96 | }
97 | log.Printf("Nonce: %d", nonce)
98 |
99 | commitments, versionedHashes, aggregatedProof, err := blobs.ComputeCommitmentsAndAggregatedProof()
100 |
101 | to := common.HexToAddress("ffb38a7a99e3e2335be83fc74b7faa19d5531243")
102 | txData := types.SignedBlobTx{
103 | Message: types.BlobTxMessage{
104 | ChainID: view.Uint256View(*uint256.NewInt(chainID.Uint64())),
105 | Nonce: view.Uint64View(nonce),
106 | Gas: 210000,
107 | GasFeeCap: view.Uint256View(*uint256.NewInt(5000000000)),
108 | GasTipCap: view.Uint256View(*uint256.NewInt(5000000000)),
109 | MaxFeePerDataGas: view.Uint256View(*uint256.NewInt(3000000000)), // needs to be at least the min fee
110 | Value: view.Uint256View(*uint256.NewInt(12345678)),
111 | To: types.AddressOptionalSSZ{Address: (*types.AddressSSZ)(&to)},
112 | BlobVersionedHashes: versionedHashes,
113 | },
114 | }
115 |
116 | wrapData := types.BlobTxWrapData{
117 | BlobKzgs: commitments,
118 | Blobs: blobs,
119 | KzgAggregatedProof: aggregatedProof,
120 | }
121 | tx := types.NewTx(&txData, types.WithTxWrapData(&wrapData))
122 | tx, err = types.SignTx(tx, signer, key)
123 | if err != nil {
124 | log.Fatalf("Error signing tx: %v", err)
125 | }
126 | err = client.SendTransaction(ctx, tx)
127 | if err != nil {
128 | log.Fatalf("Error sending tx: %v", err)
129 | }
130 | log.Printf("Transaction submitted. hash=%v", tx.Hash())
131 |
132 | log.Printf("Waiting for transaction (%v) to be included...", tx.Hash())
133 | if _, err := shared.WaitForReceipt(ctx, client, tx.Hash()); err != nil {
134 | log.Fatalf("Error waiting for transaction receipt %v: %v", tx.Hash(), err)
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/tests/util/util.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "fmt"
7 | "log"
8 | "time"
9 |
10 | "github.com/pkg/errors"
11 |
12 | "github.com/ethereum/go-ethereum/core/types"
13 | "github.com/ethereum/go-ethereum/params"
14 | ssz "github.com/prysmaticlabs/fastssz"
15 | "github.com/prysmaticlabs/prysm/v3/api/client/beacon"
16 | "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/encoder"
17 | "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
18 | consensustypes "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
19 | "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
20 | ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
21 | )
22 |
23 | func init() {
24 | encoder.MaxChunkSize = 10 << 20
25 | }
26 |
27 | func WaitForSlot(ctx context.Context, client *beacon.Client, slot consensustypes.Slot) error {
28 | for {
29 | headSlot := GetHeadSlot(ctx, client)
30 | if headSlot >= slot {
31 | break
32 | }
33 | time.Sleep(time.Second * 1)
34 | }
35 | return nil
36 | }
37 |
38 | func WaitForNextSlots(ctx context.Context, client *beacon.Client, slots consensustypes.Slot) {
39 | if err := WaitForSlot(ctx, client, GetHeadSlot(ctx, client).AddSlot(slots)); err != nil {
40 | log.Fatalf("error waiting for next slot: %v", err)
41 | }
42 | }
43 |
44 | type Body struct {
45 | BlobKzgCommitments [][]byte
46 | }
47 |
48 | type Message struct {
49 | Slot consensustypes.Slot
50 | Body Body
51 | }
52 |
53 | type Data struct {
54 | Message Message
55 | }
56 |
57 | type Block struct {
58 | Data Data
59 | }
60 |
61 | var (
62 | // Hardcoded versions for now
63 | BellatrixVersion = [4]byte{0x30, 0x00, 0x00, 0x40}
64 | CapellaVersion = [4]byte{0x40, 0x00, 0x00, 0x40}
65 | EIP4844Version = [4]byte{0x50, 0x00, 0x00, 0x40}
66 | )
67 |
68 | func GetBlock(ctx context.Context, client *beacon.Client, blockId beacon.StateOrBlockId) (*Block, error) {
69 | sb, err := client.GetState(ctx, blockId)
70 | if err != nil {
71 | return nil, errors.Wrap(err, "unable to get head state")
72 | }
73 | version, err := extractVersionFromState(sb)
74 | if err != nil {
75 | return nil, err
76 | }
77 |
78 | var m ssz.Unmarshaler
79 | switch version {
80 | case BellatrixVersion:
81 | m = ðpb.SignedBeaconBlockBellatrix{}
82 | case CapellaVersion:
83 | m = ðpb.SignedBeaconBlockCapella{}
84 | case EIP4844Version:
85 | m = ðpb.SignedBeaconBlock4844{}
86 | default:
87 | return nil, fmt.Errorf("unable to initialize beacon block for fork version=%x at blockId=%s", version, blockId)
88 | }
89 |
90 | marshaled, err := client.GetBlock(ctx, blockId)
91 | if err != nil {
92 | log.Fatalf("unable to get beacon chain block: %v", err)
93 | }
94 | err = m.UnmarshalSSZ(marshaled)
95 | if err != nil {
96 | return nil, errors.Wrap(err, "failed to unmarshal SignedBeaconBlock in UnmarshalSSZ")
97 | }
98 | blk, err := blocks.NewSignedBeaconBlock(m)
99 | if err != nil {
100 | return nil, err
101 | }
102 |
103 | kzgs, _ := blk.Block().Body().BlobKzgCommitments()
104 |
105 | var block Block
106 | block.Data.Message = Message{
107 | Slot: blk.Block().Slot(),
108 | Body: Body{
109 | BlobKzgCommitments: kzgs,
110 | },
111 | }
112 | return &block, nil
113 | }
114 |
115 | func GetHeadSlot(ctx context.Context, client *beacon.Client) consensustypes.Slot {
116 | block, err := GetBlock(ctx, client, "head")
117 | if err != nil {
118 | log.Fatalf("GetBlock error: %v", err)
119 | }
120 | return block.Data.Message.Slot
121 | }
122 |
123 | // FindBlobSlot returns the first slot containing a blob since startSlot
124 | // Panics if no such slot could be found
125 | func FindBlobSlot(ctx context.Context, client *beacon.Client, startSlot consensustypes.Slot) consensustypes.Slot {
126 | slot := startSlot
127 | endSlot := GetHeadSlot(ctx, client)
128 | for {
129 | if slot == endSlot {
130 | log.Fatalf("Unable to find beacon block containing blobs")
131 | }
132 |
133 | block, err := GetBlock(ctx, client, beacon.IdFromSlot(slot))
134 | if err != nil {
135 | log.Fatalf("beaconchainclient.GetBlock: %v", err)
136 | }
137 |
138 | if len(block.Data.Message.Body.BlobKzgCommitments) != 0 {
139 | return slot
140 | }
141 |
142 | slot = slot.Add(1)
143 | }
144 | }
145 |
146 | func AssertBlobsEquals(a, b types.Blobs) {
147 | if len(a) != len(b) {
148 | log.Fatalf("data length mismatch (%d != %d)", len(a), len(b))
149 | }
150 | for i, _ := range a {
151 | for j := 0; j < params.FieldElementsPerBlob; j++ {
152 | if !bytes.Equal(a[i][j][:], b[i][j][:]) {
153 | log.Fatal("blobs data mismatch")
154 | }
155 | }
156 | }
157 | }
158 |
159 | // extractVersionFromState reads the beacon state version from the ssz in-situ
160 | func extractVersionFromState(state []byte) ([4]byte, error) {
161 | size := 4 // field size
162 | // Using ssz offset in BeaconState:
163 | // 52 = 8 (genesis_time) + 32 (genesis_validators_root) + 8 (slot) + 4 (previous_version)
164 | offset := 52
165 | if len(state) < offset+size {
166 | return [4]byte{}, errors.New("invalid state. index out of range")
167 | }
168 | val := state[offset : offset+size]
169 | return bytesutil.ToBytes4(val), nil
170 | }
171 |
--------------------------------------------------------------------------------
/blob_tx_generator/blob.js:
--------------------------------------------------------------------------------
1 | const ethers = require("ethers")
2 | const axios = require('axios')
3 |
4 | const input = process.argv[2]
5 | const expected_kzgs = process.argv[3]
6 | const provider = new ethers.providers.JsonRpcProvider("http://localhost:8545")
7 |
8 | const BYTES_PER_FIELD_ELEMENT = 32
9 | const FIELD_ELEMENTS_PER_BLOB = 4096
10 | const USEFUL_BYTES_PER_BLOB = 32 * FIELD_ELEMENTS_PER_BLOB
11 | const MAX_BLOBS_PER_TX = 2
12 | const MAX_USEFUL_BYTES_PER_TX = (USEFUL_BYTES_PER_BLOB * MAX_BLOBS_PER_TX) - 1
13 | const BLOB_SIZE = BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB
14 |
15 | function get_padded(data, blobs_len) {
16 | let pdata = Buffer.alloc(blobs_len * USEFUL_BYTES_PER_BLOB)
17 | const datalen = Buffer.byteLength(data)
18 | pdata.fill(data, 0, datalen)
19 | // TODO: if data already fits in a pad, then ka-boom
20 | pdata[datalen] = 0x80
21 | return pdata
22 | }
23 |
24 | function get_blob(data) {
25 | let blob = Buffer.alloc(BLOB_SIZE, 'binary')
26 | for (let i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++) {
27 | let chunk = Buffer.alloc(32, 'binary')
28 | chunk.fill(data.subarray(i*31, (i+1)*31), 0, 31)
29 | blob.fill(chunk, i*32, (i+1)*32)
30 | }
31 |
32 | return blob
33 | }
34 |
35 | // ref: https://github.com/asn-d6/blobbers/blob/packing_benchmarks/src/packer_naive.rs
36 | function get_blobs(data) {
37 | data = Buffer.from(data, 'binary')
38 | const len = Buffer.byteLength(data)
39 | if (len === 0) {
40 | throw Error("invalid blob data")
41 | }
42 | if (len > MAX_USEFUL_BYTES_PER_TX) {
43 | throw Error("blob data is too large")
44 | }
45 |
46 | const blobs_len = Math.ceil(len / USEFUL_BYTES_PER_BLOB)
47 |
48 | const pdata = get_padded(data, blobs_len)
49 |
50 | let blobs = []
51 | for (let i = 0; i < blobs_len; i++) {
52 | let chunk = pdata.subarray(i*USEFUL_BYTES_PER_BLOB, (i+1)*USEFUL_BYTES_PER_BLOB)
53 | let blob = get_blob(chunk)
54 | blobs.push(blob)
55 | }
56 |
57 | return blobs
58 | }
59 |
60 | function sleep(ms) {
61 | return new Promise((resolve) => {
62 | setTimeout(resolve, ms);
63 | });
64 | }
65 |
66 | async function estimateGas(tx) {
67 | const req = {
68 | "id": "1",
69 | "jsonrpc": "2.0",
70 | "method": "eth_estimateGas",
71 | "params": [tx]
72 | }
73 | const res = await axios.post("http://localhost:8545", req)
74 | return res.data.result
75 | }
76 |
77 | async function run(data, expected_kzgs) {
78 | while (true) {
79 | const num = await provider.getBlockNumber()
80 | if (num >= 9) {
81 | break
82 | }
83 | console.log(`waiting for eip4844 proc.... bn=${num}`)
84 | await sleep(1000)
85 | }
86 | let blobs = get_blobs(data)
87 | console.log("number of blobs is " + blobs.length)
88 | const blobshex = blobs.map(x => `0x${x.toString("hex")}`)
89 |
90 | const account = ethers.Wallet.createRandom()
91 | const txData = {
92 | "from": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
93 | "to": account.address,
94 | "data": "0x",
95 | "chainId": "0x1",
96 | "blobs": blobshex,
97 | }
98 | txData["gas"] = await estimateGas(txData)
99 |
100 | const req = {
101 | "id": "1",
102 | "jsonrpc": "2.0",
103 | "method": "eth_sendTransaction",
104 | "params": [txData]
105 | }
106 | console.log(`sending to ${account.address}`)
107 | const res = await axios.post("http://localhost:8545", req)
108 | console.log(res.data)
109 | if (res.data.error) {
110 | return false
111 | }
112 |
113 | if (expected_kzgs === undefined) {
114 | return true
115 | }
116 |
117 | let blob_kzg = null
118 | try {
119 | let start = (await axios.get("http://localhost:3500/eth/v1/beacon/headers")).data.data[0].header.message.slot - 1
120 | for (let i = 0; i < 5; i++) {
121 | const res = (await axios.get(`http://localhost:3500/eth/v2/beacon/blocks/${start + i}`)).data.data.message.body.blob_kzgs
122 | if (res.length > 0) {
123 | blob_kzg = res[0]
124 | }
125 | while (true) {
126 | const current = (await axios.get("http://localhost:3500/eth/v1/beacon/headers")).data.data[0].header.message.slot - 1
127 | if (current > start + i) {
128 | break
129 | }
130 | console.log(`waiting for tx to be included in block.... bn=${current}`)
131 | await sleep(1000)
132 | }
133 | }
134 | } catch(error) {
135 | console.log(`Error retrieving blocks from ${error.config.url}: ${error.response.data}`)
136 | return false
137 | }
138 |
139 | if (blob_kzg !== expected_kzgs) {
140 | console.log(`Unexpected KZG value: expected ${expected_kzgs}, got ${blob_kzg}`)
141 | return false
142 | } else {
143 | console.log(`Found expected KZG value: ${blob_kzg}`)
144 | }
145 |
146 | return true
147 | }
148 |
149 | (async () => { process.exit((await run(input, expected_kzgs)) ? 0 : 1) })()
150 |
--------------------------------------------------------------------------------
/download/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "flag"
7 | "fmt"
8 | "io"
9 | "os"
10 | "strings"
11 |
12 | "github.com/Inphi/eip4844-interop/shared"
13 |
14 | ma "github.com/multiformats/go-multiaddr"
15 |
16 | "github.com/libp2p/go-libp2p"
17 | libp2pcore "github.com/libp2p/go-libp2p-core"
18 | "github.com/libp2p/go-libp2p-core/host"
19 | "github.com/libp2p/go-libp2p-core/peer"
20 | "github.com/libp2p/go-libp2p-core/protocol"
21 |
22 | "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p"
23 | "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/encoder"
24 | "github.com/prysmaticlabs/prysm/v3/beacon-chain/sync"
25 | types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
26 | ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
27 | )
28 |
29 | func main() {
30 | start := flag.Uint64("start", 0, "Start slot to download blobs from")
31 | count := flag.Uint64("count", 1, "Number of slots to download blobs from (default: 1)")
32 | addr := flag.String("addr", "", "P2P address to connect to")
33 | flag.Parse()
34 |
35 | if *start == 0 {
36 | panic("start parameter must be greater than 0")
37 | }
38 | if *addr == "" {
39 | panic("missing addr parameter")
40 | }
41 |
42 | ctx, cancel := context.WithCancel(context.Background())
43 | defer cancel()
44 |
45 | req := ðpb.BlobsSidecarsByRangeRequest{
46 | StartSlot: types.Slot(*start),
47 | Count: *count,
48 | }
49 |
50 | h, err := libp2p.New()
51 | if err != nil {
52 | panic(err)
53 | }
54 | defer func() {
55 | _ = h.Close()
56 | }()
57 |
58 | multiaddr, err := getMultiaddr(ctx, h, *addr)
59 | if err != nil {
60 | panic(err)
61 | }
62 |
63 | addrInfo, err := peer.AddrInfoFromP2pAddr(multiaddr)
64 | if err != nil {
65 | panic(err)
66 | }
67 |
68 | err = h.Connect(ctx, *addrInfo)
69 | if err != nil {
70 | panic(err)
71 | }
72 |
73 | // Hack to ensure that we are able to download blob chunks with larger chunk sizes (which is 10 MiB post-bellatrix)
74 | encoder.MaxChunkSize = 10 << 20
75 | sidecars, err := sendBlobsSidecarsByRangeRequest(ctx, h, encoder.SszNetworkEncoder{}, addrInfo.ID, req)
76 | if err != nil {
77 | panic(err)
78 | }
79 |
80 | anyBlobs := false
81 | for _, sidecar := range sidecars {
82 | if sidecar.Blobs == nil || len(sidecar.Blobs) == 0 {
83 | continue
84 | }
85 | anyBlobs = true
86 | for _, blob := range sidecar.Blobs {
87 | data := shared.DecodeFlatBlob(blob.Data)
88 | _, _ = os.Stdout.Write(data)
89 | }
90 |
91 | // stop after the first sidecar with blobs:
92 | break
93 | }
94 |
95 | if !anyBlobs {
96 | panic(fmt.Sprintf("No blobs found in requested slots, sidecar count: %d", len(sidecars)))
97 | }
98 | }
99 |
100 | func getMultiaddr(ctx context.Context, h host.Host, addr string) (ma.Multiaddr, error) {
101 | multiaddr, err := ma.NewMultiaddr(addr)
102 | if err != nil {
103 | return nil, err
104 | }
105 | _, id := peer.SplitAddr(multiaddr)
106 | if id != "" {
107 | return multiaddr, nil
108 | }
109 | // peer ID wasn't provided, look it up
110 | id, err = retrievePeerID(ctx, h, addr)
111 | if err != nil {
112 | return nil, err
113 | }
114 | return ma.NewMultiaddr(fmt.Sprintf("%s/p2p/%s", addr, string(id)))
115 | }
116 |
117 | // Helper for retrieving the peer ID from a security error... obviously don't use this in production!
118 | // See https://github.com/libp2p/go-libp2p-noise/blob/v0.3.0/handshake.go#L250
119 | func retrievePeerID(ctx context.Context, h host.Host, addr string) (peer.ID, error) {
120 | incorrectPeerID := "16Uiu2HAmSifdT5QutTsaET8xqjWAMPp4obrQv7LN79f2RMmBe3nY"
121 | addrInfo, err := peer.AddrInfoFromString(fmt.Sprintf("%s/p2p/%s", addr, incorrectPeerID))
122 | if err != nil {
123 | return "", err
124 | }
125 | err = h.Connect(ctx, *addrInfo)
126 | if err == nil {
127 | return "", errors.New("unexpected successful connection")
128 | }
129 | if strings.Contains(err.Error(), "but remote key matches") {
130 | split := strings.Split(err.Error(), " ")
131 | return peer.ID(split[len(split)-1]), nil
132 | }
133 | return "", err
134 | }
135 |
136 | func sendBlobsSidecarsByRangeRequest(ctx context.Context, h host.Host, encoding encoder.NetworkEncoding, pid peer.ID, req *ethpb.BlobsSidecarsByRangeRequest) ([]*ethpb.BlobsSidecar, error) {
137 | topic := fmt.Sprintf("%s%s", p2p.RPCBlobsSidecarsByRangeTopicV1, encoding.ProtocolSuffix())
138 |
139 | stream, err := h.NewStream(ctx, pid, protocol.ID(topic))
140 | if err != nil {
141 | return nil, err
142 | }
143 | defer func() {
144 | _ = stream.Close()
145 | }()
146 |
147 | if _, err := encoding.EncodeWithMaxLength(stream, req); err != nil {
148 | _ = stream.Reset()
149 | return nil, err
150 | }
151 |
152 | if err := stream.CloseWrite(); err != nil {
153 | _ = stream.Reset()
154 | return nil, err
155 | }
156 |
157 | var blobsSidecars []*ethpb.BlobsSidecar
158 | for {
159 | blobs, err := readChunkedBlobsSidecar(stream, encoding)
160 | if errors.Is(err, io.EOF) {
161 | break
162 | }
163 | if err != nil {
164 | return nil, err
165 | }
166 | blobsSidecars = append(blobsSidecars, blobs)
167 | }
168 | return blobsSidecars, nil
169 | }
170 |
171 | func readChunkedBlobsSidecar(stream libp2pcore.Stream, encoding encoder.NetworkEncoding) (*ethpb.BlobsSidecar, error) {
172 | code, errMsg, err := sync.ReadStatusCode(stream, encoding)
173 | if err != nil {
174 | return nil, err
175 | }
176 | if code != 0 {
177 | return nil, errors.New(errMsg)
178 | }
179 | sidecar := new(ethpb.BlobsSidecar)
180 | err = encoding.DecodeWithMaxLength(stream, sidecar)
181 | return sidecar, err
182 | }
183 |
--------------------------------------------------------------------------------
/tests/initial-sync/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "log"
6 | "math/big"
7 | "os"
8 | "time"
9 |
10 | "github.com/Inphi/eip4844-interop/shared"
11 | "github.com/Inphi/eip4844-interop/tests/ctrl"
12 | "github.com/Inphi/eip4844-interop/tests/util"
13 | "github.com/ethereum/go-ethereum/common"
14 | "github.com/ethereum/go-ethereum/core/types"
15 | "github.com/ethereum/go-ethereum/crypto"
16 | "github.com/ethereum/go-ethereum/ethclient"
17 | "github.com/holiman/uint256"
18 | "github.com/protolambda/ztyp/view"
19 | "golang.org/x/sync/errgroup"
20 | )
21 |
22 | func GetBlobs() types.Blobs {
23 | // dummy data for the test
24 | return shared.EncodeBlobs([]byte("EKANS"))
25 | }
26 |
27 | // Asserts blob syncing functionality during initial-sync
28 | // 1. Start a single EL/CL node
29 | // 2. Upload blobs
30 | // 3. Wait for blobs to be available
31 | // 4. Start follower EL/CL nodes
32 | // 5. Download blobs from follower
33 | // 6. Asserts that downloaded blobs match the upload
34 | // 7. Asserts execution and beacon block attributes
35 | func main() {
36 | ctx, cancel := context.WithTimeout(context.Background(), time.Minute*20)
37 | defer cancel()
38 |
39 | ctrl.StopDevnet()
40 |
41 | clientName := "prysm"
42 | if len(os.Args) > 1 {
43 | clientName = os.Args[1]
44 | }
45 | env := ctrl.InitEnvForClient(clientName)
46 |
47 | g, gctx := errgroup.WithContext(ctx)
48 | g.Go(func() error {
49 | return env.GethNode.Start(gctx)
50 | })
51 | g.Go(func() error {
52 | return env.BeaconNode.Start(gctx)
53 | })
54 | g.Go(func() error {
55 | return env.ValidatorNode.Start(gctx)
56 | })
57 | if err := g.Wait(); err != nil {
58 | log.Fatalf("failed to start services: %v", err)
59 | }
60 | ctrl.WaitForShardingFork()
61 | ctrl.WaitForEip4844ForkEpoch()
62 |
63 | ethClient, err := ctrl.GetExecutionClient(ctx)
64 | if err != nil {
65 | log.Fatalf("unable to get execution client: %v", err)
66 | }
67 | beaconClient, err := ctrl.GetBeaconNodeClient(ctx)
68 | if err != nil {
69 | log.Fatalf("unable to get beacon client: %v", err)
70 | }
71 |
72 | // Retrieve the current slot to being our blobs search on the beacon chain
73 | startSlot := util.GetHeadSlot(ctx, beaconClient)
74 |
75 | blobs := GetBlobs()
76 | chainID := env.GethChainConfig.ChainID
77 | UploadBlobs(ctx, ethClient, chainID, blobs)
78 | util.WaitForNextSlots(ctx, beaconClient, 1)
79 | blobSlot := util.FindBlobSlot(ctx, beaconClient, startSlot)
80 |
81 | // Wait a bit to induce substantial initial-sync in the beacon node follower
82 | util.WaitForNextSlots(ctx, beaconClient, 10)
83 |
84 | g.Go(func() error {
85 | return env.GethNode2.Start(ctx)
86 | })
87 | g.Go(func() error {
88 | return env.BeaconNodeFollower.Start(ctx)
89 | })
90 | if err := g.Wait(); err != nil {
91 | log.Fatalf("failed to start services: %v", err)
92 | }
93 |
94 | beaconNodeFollowerClient, err := ctrl.GetBeaconNodeFollowerClient(ctx)
95 | if err != nil {
96 | log.Fatalf("failed to get beacon node follower client: %v", err)
97 | }
98 |
99 | syncSlot := util.GetHeadSlot(ctx, beaconClient)
100 | if err := ctrl.WaitForSlotWithClient(ctx, beaconNodeFollowerClient, syncSlot); err != nil {
101 | log.Fatalf("unable to wait for beacon follower sync: %v", err)
102 | }
103 |
104 | log.Printf("checking blob from beacon node")
105 | beaconMA, err := shared.GetBeaconMultiAddress()
106 | if err != nil {
107 | log.Fatalf("Unable to get beacon mutliaddress")
108 | }
109 | downloadedData := util.DownloadBlobs(ctx, blobSlot, 1, beaconMA)
110 | downloadedBlobs := shared.EncodeBlobs(downloadedData)
111 | util.AssertBlobsEquals(blobs, downloadedBlobs)
112 |
113 | log.Printf("checking blob from beacon node follower")
114 | time.Sleep(time.Second * 2 * time.Duration(env.BeaconChainConfig.SecondsPerSlot)) // wait a bit for sync
115 | beaconFollowerMA, err := shared.GetBeaconFollowerMultiAddress()
116 | if err != nil {
117 | log.Fatalf("Unable to get beacon follower mutliaddress")
118 | }
119 | downloadedData = util.DownloadBlobs(ctx, blobSlot, 1, beaconFollowerMA)
120 | downloadedBlobs = shared.EncodeBlobs(downloadedData)
121 | util.AssertBlobsEquals(blobs, downloadedBlobs)
122 | }
123 |
124 | func UploadBlobs(ctx context.Context, client *ethclient.Client, chainId *big.Int, blobs types.Blobs) {
125 | signer := types.NewDankSigner(chainId)
126 |
127 | key, err := crypto.HexToECDSA(shared.PrivateKey)
128 | if err != nil {
129 | log.Fatalf("Failed to load private key: %v", err)
130 | }
131 |
132 | nonce, err := client.PendingNonceAt(ctx, crypto.PubkeyToAddress(key.PublicKey))
133 | if err != nil {
134 | log.Fatalf("Error getting nonce: %v", err)
135 | }
136 | log.Printf("Nonce: %d", nonce)
137 |
138 | commitments, versionedHashes, aggregatedProof, err := blobs.ComputeCommitmentsAndAggregatedProof()
139 |
140 | to := common.HexToAddress("ffb38a7a99e3e2335be83fc74b7faa19d5531243")
141 | txData := types.SignedBlobTx{
142 | Message: types.BlobTxMessage{
143 | ChainID: view.Uint256View(*uint256.NewInt(chainId.Uint64())),
144 | Nonce: view.Uint64View(nonce),
145 | Gas: 210000,
146 | GasFeeCap: view.Uint256View(*uint256.NewInt(5000000000)),
147 | GasTipCap: view.Uint256View(*uint256.NewInt(5000000000)),
148 | MaxFeePerDataGas: view.Uint256View(*uint256.NewInt(3000000000)), // needs to be at least the min fee
149 | Value: view.Uint256View(*uint256.NewInt(12345678)),
150 | To: types.AddressOptionalSSZ{Address: (*types.AddressSSZ)(&to)},
151 | BlobVersionedHashes: versionedHashes,
152 | },
153 | }
154 |
155 | wrapData := types.BlobTxWrapData{
156 | BlobKzgs: commitments,
157 | Blobs: blobs,
158 | KzgAggregatedProof: aggregatedProof,
159 | }
160 | tx := types.NewTx(&txData, types.WithTxWrapData(&wrapData))
161 | tx, err = types.SignTx(tx, signer, key)
162 | if err != nil {
163 | log.Fatalf("Error signing tx: %v", err)
164 | }
165 | err = client.SendTransaction(ctx, tx)
166 | if err != nil {
167 | log.Fatalf("Error sending tx: %v", err)
168 | }
169 | log.Printf("Transaction submitted. hash=%v", tx.Hash())
170 |
171 | log.Printf("Waiting for transaction (%v) to be included...", tx.Hash())
172 | if _, err := shared.WaitForReceipt(ctx, client, tx.Hash()); err != nil {
173 | log.Fatalf("Error waiting for transaction receipt %v: %v", tx.Hash(), err)
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | # The flag below may be needed if blst throws SIGILL, which happens with certain (older) CPUs
4 | env:
5 | CGO_CFLAGS: '-O -D__BLST_PORTABLE__'
6 |
7 | on:
8 | push:
9 | branches: ['master']
10 | pull_request:
11 | branches: ['master']
12 |
13 | jobs:
14 | test-prysm:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v3
19 | with:
20 | submodules: recursive
21 |
22 | - uses: actions/setup-go@v3
23 | with:
24 | # go 1.19 is incompatible with the github.com/lucas-clemente/quic-go v0.27.2 dependency
25 | go-version: '1.18'
26 |
27 | - uses: jpribyl/action-docker-layer-caching@v0.1.1
28 | # Ignore the failure of a step and avoid terminating the job.
29 | continue-on-error: true
30 | with:
31 | key: docker-cache-${{ hashFiles('.git/modules/prysm/*/HEAD', '.git/modules/geth/*/HEAD') }}
32 |
33 | # build images as a separate step to better utilize caching
34 | # TODO: change to pure "docker-compose build" once we can successfully build lighthouse and lodster
35 | - run: docker-compose build genesis-generator bootnode prysm-beacon-node prysm-beacon-node-follower prysm-validator-node execution-node execution-node-2 && go mod download
36 |
37 | - name: Prysm - Run pre-EIP4844 tests
38 | timeout-minutes: 60
39 | run: make pre4844-test el=prysm
40 |
41 | # TODO: A bit redundant. combine this test with fee-market
42 | - name: Prysm - Run Blob transaction tests
43 | timeout-minutes: 60
44 | run: make blobtx-test el=prysm
45 |
46 | - name: Prysm - Run Fee market spec tests
47 | timeout-minutes: 60
48 | run: make fee-market-test el=prysm
49 |
50 | - name: Prysm - Run Initial sync tests
51 | timeout-minutes: 60
52 | run: make initial-sync-test el=prysm
53 |
54 | - name: Collect docker logs on failure
55 | if: failure()
56 | uses: jwalton/gh-docker-logs@v1
57 | with:
58 | images: 'eip4844-interop_prysm-beacon-node-follower,eip4844-interop_prysm-beacon-node,eip4844-interop_execution-node-2,eip4844-interop_execution-node,eip4844-interop_prysm-validator-node'
59 | dest: './logs'
60 |
61 | - name: Tar logs
62 | if: failure()
63 | run: tar cvzf ./logs.tgz ./logs
64 |
65 | - name: Upload logs to GitHub
66 | if: failure()
67 | uses: actions/upload-artifact@master
68 | with:
69 | name: logs.tgz
70 | path: ./logs.tgz
71 |
72 | test-lodestar:
73 | runs-on: ubuntu-latest
74 | steps:
75 | - name: Checkout
76 | uses: actions/checkout@v3
77 | with:
78 | submodules: recursive
79 |
80 | - uses: actions/setup-go@v3
81 | with:
82 | # go 1.19 is incompatible with the github.com/lucas-clemente/quic-go v0.27.2 dependency
83 | go-version: '1.18'
84 |
85 | - uses: jpribyl/action-docker-layer-caching@v0.1.1
86 | # Ignore the failure of a step and avoid terminating the job.
87 | continue-on-error: true
88 | with:
89 | key: docker-cache-${{ hashFiles('.git/modules/lodestar/*/HEAD', '.git/modules/geth/*/HEAD') }}
90 |
91 | - name: Lodestar - Run pre-EIP4844 tests
92 | run: go run ./tests/pre-4844 lodestar
93 |
94 | # - name: Lodestar - Run Blob transaction tests
95 | # run: go run ./tests/blobtx lodestar
96 |
97 | # - name: Lodestar - Run Fee market spec tests
98 | # run: go run ./tests/fee-market lodestar
99 |
100 | # - name: Lodestar - Run Initial sync tests
101 | # run: go run ./tests/initial-sync lodestar
102 |
103 | - name: Collect docker logs on failure
104 | if: failure()
105 | uses: jwalton/gh-docker-logs@v1
106 | with:
107 | images: 'eip4844-interop_prysm-beacon-node-follower,eip4844-interop_prysm-beacon-node,eip4844-interop_execution-node-2,eip4844-interop_execution-node,eip4844-interop_prysm-validator-node'
108 | dest: './logs'
109 |
110 | - name: Tar logs
111 | if: failure()
112 | run: tar cvzf ./logs.tgz ./logs
113 |
114 | - name: Upload logs to GitHub
115 | if: failure()
116 | uses: actions/upload-artifact@master
117 | with:
118 | name: logs.tgz
119 | path: ./logs.tgz
120 |
121 | test-lighthouse:
122 | runs-on: ubuntu-latest
123 | steps:
124 | - name: Checkout
125 | uses: actions/checkout@v3
126 | with:
127 | submodules: recursive
128 |
129 | - uses: actions/setup-go@v3
130 | with:
131 | # go 1.19 is incompatible with the github.com/lucas-clemente/quic-go v0.27.2 dependency
132 | go-version: '1.18'
133 |
134 | - uses: jpribyl/action-docker-layer-caching@v0.1.1
135 | # Ignore the failure of a step and avoid terminating the job.
136 | continue-on-error: true
137 | with:
138 | key: docker-cache-${{ hashFiles('.git/modules/lighthouse/*/HEAD', '.git/modules/geth/*/HEAD') }}
139 |
140 | # - name: Lighthouse - Run pre-EIP4844 tests
141 | # timeout-minutes: 60
142 | # run: go run ./tests/pre-4844 lighthouse
143 |
144 | - name: Lighthouse - Run Blob transaction tests
145 | timeout-minutes: 60
146 | run: go run ./tests/blobtx lighthouse
147 |
148 | - name: Lighthouse - Run Fee market spec tests
149 | timeout-minutes: 60
150 | run: go run ./tests/fee-market lighthouse
151 |
152 | - name: Lighthouse - Run Initial sync tests
153 | timeout-minutes: 60
154 | run: go run ./tests/initial-sync lighthouse
155 |
156 | - name: Collect docker logs on failure
157 | if: failure()
158 | uses: jwalton/gh-docker-logs@v1
159 | with:
160 | images: 'eip4844-interop_prysm-beacon-node-follower,eip4844-interop_prysm-beacon-node,eip4844-interop_execution-node-2,eip4844-interop_execution-node,eip4844-interop_prysm-validator-node'
161 | dest: './logs'
162 |
163 | - name: Tar logs
164 | if: failure()
165 | run: tar cvzf ./logs.tgz ./logs
166 |
167 | - name: Upload logs to GitHub
168 | if: failure()
169 | uses: actions/upload-artifact@master
170 | with:
171 | name: logs.tgz
172 | path: ./logs.tgz
173 |
--------------------------------------------------------------------------------
/tests/ctrl/bootstrap.go:
--------------------------------------------------------------------------------
1 | package ctrl
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "io/ioutil"
7 | "log"
8 | "time"
9 |
10 | "github.com/Inphi/eip4844-interop/shared"
11 | "github.com/Inphi/eip4844-interop/tests/util"
12 | "github.com/ethereum/go-ethereum/core"
13 | "github.com/ethereum/go-ethereum/params"
14 | "github.com/prysmaticlabs/prysm/v3/api/client/beacon"
15 | types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
16 | "golang.org/x/sync/errgroup"
17 | "gopkg.in/yaml.v2"
18 | )
19 |
20 | var consensusClientEnvironment *TestEnvironment
21 |
22 | // Stateful. InitE2ETest sets this.
23 | var client string
24 |
25 | func GetEnv() *TestEnvironment {
26 | return consensusClientEnvironment
27 | }
28 |
29 | func InitEnvForClient(clientName string) *TestEnvironment {
30 | client = clientName
31 | switch client {
32 | case "prysm":
33 | consensusClientEnvironment = newPrysmTestEnvironment()
34 | case "lodestar":
35 | consensusClientEnvironment = newLodestarTestEnvironment()
36 | case "lighthouse":
37 | consensusClientEnvironment = newLighthouseTestEnvironment()
38 | default:
39 | log.Fatalf("unknown client %s", clientName)
40 | }
41 | return consensusClientEnvironment
42 | }
43 |
44 | func InitE2ETest(clientName string) {
45 | ctx := context.Background()
46 | if err := StopDevnet(); err != nil {
47 | log.Fatalf("unable to stop devnet: %v", err)
48 | }
49 |
50 | env := InitEnvForClient(clientName)
51 | if err := env.StartAll(ctx); err != nil {
52 | log.Fatalf("unable to start environment: %v", err)
53 | }
54 | }
55 |
56 | func WaitForShardingFork() {
57 | ctx := context.Background()
58 |
59 | config := GetEnv().GethChainConfig
60 | eip4844ForkTime := config.ShardingForkTime
61 | if eip4844ForkTime == nil {
62 | log.Fatalf("shardingForkTime is not set in configuration")
63 | }
64 |
65 | stallTimeout := 60 * time.Minute
66 |
67 | client, err := GetExecutionClient(ctx)
68 | if err != nil {
69 | log.Fatalf("unable to retrive beacon node client: %v", err)
70 | }
71 |
72 | log.Printf("waiting for sharding fork time...")
73 | var lastBn uint64
74 | lastUpdate := time.Now()
75 | for {
76 | b, err := client.BlockByNumber(ctx, nil)
77 | if err != nil {
78 | log.Fatalf("ethclient.BlockByNumber: %v", err)
79 | }
80 | if b.Time() >= *eip4844ForkTime {
81 | break
82 | }
83 | // Chain stall detection
84 | if b.NumberU64() != lastBn {
85 | lastBn = b.NumberU64()
86 | lastUpdate = time.Now()
87 | } else if time.Since(lastUpdate) > stallTimeout {
88 | log.Fatalf("Chain is stalled on block %v", b.NumberU64())
89 | }
90 | time.Sleep(time.Second * 1)
91 | }
92 | }
93 |
94 | func ReadGethChainConfig() *params.ChainConfig {
95 | return ReadGethChainConfigFromPath(shared.GethChainConfigFilepath())
96 | }
97 |
98 | func ReadGethChainConfigFromPath(path string) *params.ChainConfig {
99 | data, err := ioutil.ReadFile(path)
100 | if err != nil {
101 | log.Fatalf("unable to read geth chain config file at %v: %v", path, err)
102 | }
103 | var genesis core.Genesis
104 | if err := json.Unmarshal(data, &genesis); err != nil {
105 | log.Fatalf("invalid chain config at %v: %v", path, err)
106 | }
107 | return genesis.Config
108 | }
109 |
110 | func ReadBeaconChainConfig() *BeaconChainConfig {
111 | return ReadBeaconChainConfigFromPath(shared.BeaconChainConfigFilepath())
112 | }
113 |
114 | func ReadBeaconChainConfigFromPath(path string) *BeaconChainConfig {
115 | data, err := ioutil.ReadFile(path)
116 | if err != nil {
117 | log.Fatalf("unable to read beacon chain config file at %v: %v", path, err)
118 | }
119 | var config BeaconChainConfig
120 | if err := yaml.Unmarshal(data, &config); err != nil {
121 | log.Fatalf("invalid beacon chain config file at %v: %v", path, err)
122 | }
123 | return &config
124 | }
125 |
126 | func WaitForSlot(ctx context.Context, slot types.Slot) error {
127 | client, err := GetBeaconNodeClient(ctx)
128 | if err != nil {
129 | return err
130 | }
131 | return WaitForSlotWithClient(ctx, client, slot)
132 | }
133 |
134 | func WaitForSlotWithClient(ctx context.Context, client *beacon.Client, slot types.Slot) error {
135 | for {
136 | headSlot := util.GetHeadSlot(ctx, client)
137 | if headSlot >= slot {
138 | break
139 | }
140 | time.Sleep(time.Second * time.Duration(GetEnv().BeaconChainConfig.SecondsPerSlot))
141 | }
142 | return nil
143 | }
144 |
145 | func WaitForEip4844ForkEpoch() {
146 | log.Println("waiting for eip4844 fork epoch...")
147 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
148 | defer cancel()
149 |
150 | config := GetEnv().BeaconChainConfig
151 | // TODO: query /eth/v1/config/spec for time parameters
152 | eip4844Slot := config.Eip4844ForkEpoch * config.SlotsPerEpoch
153 | if err := WaitForSlot(ctx, types.Slot(eip4844Slot)); err != nil {
154 | log.Fatal(err)
155 | }
156 | }
157 |
158 | type BeaconChainConfig struct {
159 | AltairForkEpoch uint64 `yaml:"ALTAIR_FORK_EPOCH"`
160 | BellatrixForkEpoch uint64 `yaml:"BELLATRIX_FORK_EPOCH"`
161 | Eip4844ForkEpoch uint64 `yaml:"EIP4844_FORK_EPOCH"`
162 | SlotsPerEpoch uint64 `yaml:"SLOTS_PER_EPOCH"`
163 | SecondsPerSlot uint64 `yaml:"SECONDS_PER_SLOT"`
164 | TerminalTotalDifficulty uint64 `yaml:"TERMINAL_TOTAL_DIFFICULTY"`
165 | BellatrixForkVersion string `yaml:"BELLATRIX_FORK_VERSION"`
166 | CapellaForkVersion string `yaml:"CAPELLA_FORK_VERSION"`
167 | EIP4844ForkVersion string `yaml:"EIP4844_FORK_VERSION"`
168 | }
169 |
170 | type TestEnvironment struct {
171 | GethChainConfig *params.ChainConfig
172 | BeaconChainConfig *BeaconChainConfig
173 | BeaconNode Service
174 | GethNode Service
175 | ValidatorNode Service
176 | BeaconNodeFollower Service
177 | GethNode2 Service
178 | }
179 |
180 | func setupGeneratedConfigs() {
181 | if err := StartServices("genesis-generator"); err != nil {
182 | log.Fatalf("failed to start genesis-generator service: %v", err)
183 | }
184 | // TODO: it takes a moment for the docker daemon to synchronize files
185 | time.Sleep(time.Second * 10)
186 | }
187 |
188 | func newPrysmTestEnvironment() *TestEnvironment {
189 | setupGeneratedConfigs()
190 |
191 | shared.BeaconAPI = "localhost:3500"
192 | shared.BeaconFollowerAPI = "localhost:3501"
193 |
194 | clientName := "prysm"
195 | return &TestEnvironment{
196 | BeaconChainConfig: ReadBeaconChainConfig(),
197 | BeaconNode: NewBeaconNode(clientName),
198 | BeaconNodeFollower: NewBeaconNodeFollower(clientName),
199 | ValidatorNode: NewValidatorNode(clientName),
200 | GethChainConfig: ReadGethChainConfig(),
201 | GethNode: NewGethNode(),
202 | GethNode2: NewGethNode2(),
203 | }
204 | }
205 |
206 | func newLodestarTestEnvironment() *TestEnvironment {
207 | clientName := "lodestar"
208 | return &TestEnvironment{
209 | BeaconChainConfig: ReadBeaconChainConfig(),
210 | BeaconNode: NewBeaconNode(clientName),
211 | BeaconNodeFollower: NewBeaconNodeFollower(clientName),
212 | GethChainConfig: ReadGethChainConfig(),
213 | GethNode: NewGethNode(),
214 | GethNode2: NewGethNode2(),
215 | }
216 | }
217 |
218 | func newLighthouseTestEnvironment() *TestEnvironment {
219 | setupGeneratedConfigs()
220 |
221 | clientName := "lighthouse"
222 | return &TestEnvironment{
223 | BeaconChainConfig: ReadBeaconChainConfig(),
224 | BeaconNode: NewBeaconNode(clientName),
225 | BeaconNodeFollower: NewBeaconNodeFollower(clientName),
226 | ValidatorNode: NewValidatorNode(clientName),
227 | GethChainConfig: ReadGethChainConfig(),
228 | GethNode: NewGethNode(),
229 | GethNode2: NewGethNode2(),
230 | }
231 | }
232 |
233 | func (env *TestEnvironment) StartAll(ctx context.Context) error {
234 | g, ctx := errgroup.WithContext(ctx)
235 | g.Go(func() error {
236 | return env.BeaconNode.Start(ctx)
237 | })
238 | g.Go(func() error {
239 | if env.ValidatorNode != nil {
240 | return env.ValidatorNode.Start(ctx)
241 | }
242 | return nil
243 | })
244 | g.Go(func() error {
245 | if env.BeaconNodeFollower != nil {
246 | return env.BeaconNodeFollower.Start(ctx)
247 | }
248 | return nil
249 | })
250 | g.Go(func() error {
251 | return env.GethNode.Start(ctx)
252 | })
253 | g.Go(func() error {
254 | if env.GethNode2 != nil {
255 | return env.GethNode2.Start(ctx)
256 | }
257 | return nil
258 | })
259 | return g.Wait()
260 | }
261 |
--------------------------------------------------------------------------------
/tests/fee-market/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "log"
7 | "math/big"
8 | "os"
9 | "sort"
10 | "sync"
11 | "time"
12 |
13 | "github.com/Inphi/eip4844-interop/shared"
14 | "github.com/Inphi/eip4844-interop/tests/ctrl"
15 | "github.com/Inphi/eip4844-interop/tests/util"
16 | "github.com/ethereum/go-ethereum/common"
17 | "github.com/ethereum/go-ethereum/consensus/misc"
18 | "github.com/ethereum/go-ethereum/core/types"
19 | "github.com/ethereum/go-ethereum/crypto"
20 | "github.com/ethereum/go-ethereum/ethclient"
21 | "github.com/holiman/uint256"
22 | "github.com/protolambda/ztyp/view"
23 | "github.com/prysmaticlabs/prysm/v3/api/client/beacon"
24 | consensustypes "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
25 | )
26 |
27 | func GetBlob() types.Blobs {
28 | // dummy data for the test
29 | return shared.EncodeBlobs([]byte("EKANS"))
30 | }
31 |
32 | // 1. Uploads *multiple* blobs
33 | // 2. Downloads blobs
34 | // 3. Asserts that downloaded blobs match the upload
35 | // 4. Asserts execution and beacon block attributes
36 | func main() {
37 | clientName := "prysm"
38 | if len(os.Args) > 1 {
39 | clientName = os.Args[1]
40 | }
41 | ctrl.InitE2ETest(clientName)
42 | ctrl.WaitForShardingFork()
43 | ctrl.WaitForEip4844ForkEpoch()
44 | env := ctrl.GetEnv()
45 | ctx, cancel := context.WithTimeout(context.Background(), time.Minute*20)
46 | defer cancel()
47 |
48 | ethClient, err := ctrl.GetExecutionClient(ctx)
49 | if err != nil {
50 | log.Fatalf("unable to get execution client: %v", err)
51 | }
52 | beaconClient, err := ctrl.GetBeaconNodeClient(ctx)
53 | if err != nil {
54 | log.Fatalf("unable to get beacon client: %v", err)
55 | }
56 |
57 | blobsData := make([]types.Blobs, 20)
58 | for i := range blobsData {
59 | blobsData[i] = GetBlob()
60 | }
61 |
62 | // Retrieve the current slot to being our blobs search on the beacon chain
63 | startSlot := util.GetHeadSlot(ctx, beaconClient)
64 |
65 | // Send multiple transactions at the same time to induce non-zero excess_blobs
66 | chainID := env.GethChainConfig.ChainID
67 | UploadBlobsAndCheckBlockHeader(ctx, ethClient, chainID, blobsData)
68 |
69 | util.WaitForNextSlots(ctx, beaconClient, 1)
70 | util.WaitForNextSlots(ctx, beaconClient, 1)
71 |
72 | log.Print("blobs uploaded. finding blocks with blobs")
73 |
74 | blocks := FindBlocksWithBlobs(ctx, beaconClient, startSlot)
75 |
76 | log.Printf("checking blob from beacon node")
77 | ma, err := shared.GetBeaconMultiAddress()
78 | if err != nil {
79 | log.Fatalf("unable to get beacon multiaddr: %v", err)
80 | }
81 | var downloadedData []byte
82 | for _, b := range blocks {
83 | data := util.DownloadBlobs(ctx, b.Data.Message.Slot, 1, ma)
84 | downloadedData = append(downloadedData, data...)
85 | }
86 |
87 | flatBlobs := FlattenBlobs(blobsData)
88 |
89 | if !bytes.Equal(flatBlobs, downloadedData) {
90 | log.Fatalf("mismatch %d %v", len(flatBlobs), len(downloadedData))
91 | }
92 |
93 | log.Printf("checking blob from beacon node follower")
94 | time.Sleep(time.Second * 2 * time.Duration(env.BeaconChainConfig.SecondsPerSlot)) // wait a bit for sync
95 |
96 | downloadedData = nil
97 | maFollower, err := shared.GetBeaconFollowerMultiAddress()
98 | if err != nil {
99 | log.Fatalf("unable to get beacon multiaddr: %v", err)
100 | }
101 | for _, b := range blocks {
102 | data := util.DownloadBlobs(ctx, b.Data.Message.Slot, 1, maFollower)
103 | downloadedData = append(downloadedData, data...)
104 | }
105 | if !bytes.Equal(flatBlobs, downloadedData) {
106 | log.Fatalf("mismatch %d %v", len(flatBlobs), len(downloadedData))
107 | }
108 | }
109 |
110 | func FlattenBlobs(blobsData []types.Blobs) []byte {
111 | var out []byte
112 | for _, blobs := range blobsData {
113 | for _, blob := range blobs {
114 | rawBlob := make([][]byte, len(blob))
115 | for i := range blob {
116 | rawBlob[i] = blob[i][:]
117 | }
118 | decoded := shared.DecodeBlob(rawBlob)
119 | out = append(out, decoded...)
120 | }
121 | }
122 | return out
123 | }
124 |
125 | func UploadBlobsAndCheckBlockHeader(ctx context.Context, client *ethclient.Client, chainId *big.Int, blobsData []types.Blobs) {
126 | signer := types.NewDankSigner(chainId)
127 |
128 | key, err := crypto.HexToECDSA(shared.PrivateKey)
129 | if err != nil {
130 | log.Fatalf("Failed to load private key: %v", err)
131 | }
132 |
133 | nonce, err := client.PendingNonceAt(ctx, crypto.PubkeyToAddress(key.PublicKey))
134 | if err != nil {
135 | log.Fatalf("Error getting nonce: %v", err)
136 | }
137 | log.Printf("Nonce: %d", nonce)
138 |
139 | var txs []*types.Transaction
140 | for i := range blobsData {
141 | blobs := blobsData[i]
142 | commitments, versionedHashes, aggregatedProof, err := blobs.ComputeCommitmentsAndAggregatedProof()
143 | to := common.HexToAddress("ffb38a7a99e3e2335be83fc74b7faa19d5531243")
144 | txData := types.SignedBlobTx{
145 | Message: types.BlobTxMessage{
146 | ChainID: view.Uint256View(*uint256.NewInt(chainId.Uint64())),
147 | Nonce: view.Uint64View(nonce + uint64(i)),
148 | Gas: 210000,
149 | GasFeeCap: view.Uint256View(*uint256.NewInt(5000000000)),
150 | GasTipCap: view.Uint256View(*uint256.NewInt(5000000000)),
151 | MaxFeePerDataGas: view.Uint256View(*uint256.NewInt(3000000000)), // needs to be at least the min fee
152 | Value: view.Uint256View(*uint256.NewInt(12345678)),
153 | To: types.AddressOptionalSSZ{Address: (*types.AddressSSZ)(&to)},
154 | BlobVersionedHashes: versionedHashes,
155 | },
156 | }
157 |
158 | wrapData := types.BlobTxWrapData{
159 | BlobKzgs: commitments,
160 | Blobs: blobs,
161 | KzgAggregatedProof: aggregatedProof,
162 | }
163 | tx := types.NewTx(&txData, types.WithTxWrapData(&wrapData))
164 | tx, err = types.SignTx(tx, signer, key)
165 | if err != nil {
166 | log.Fatalf("Error signing tx: %v", err)
167 | }
168 | txs = append(txs, tx)
169 | }
170 |
171 | receipts := make(chan *types.Receipt, len(txs))
172 | var wg sync.WaitGroup
173 | wg.Add(len(txs))
174 | for _, tx := range txs {
175 | tx := tx
176 | go func() {
177 | defer wg.Done()
178 | err := client.SendTransaction(ctx, tx)
179 | if err != nil {
180 | log.Fatalf("Error sending tx: %v", err)
181 | }
182 |
183 | log.Printf("Waiting for transaction (%v) to be included...", tx.Hash())
184 |
185 | receipt, err := shared.WaitForReceipt(ctx, client, tx.Hash())
186 | if err != nil {
187 | log.Fatalf("Error waiting for transaction receipt %v: %v", tx.Hash(), err)
188 | }
189 | receipts <- receipt
190 | }()
191 | }
192 | wg.Wait()
193 | close(receipts)
194 |
195 | log.Printf("checking mined blocks...")
196 | blockNumbers := make(map[uint64]bool)
197 | var blocks []*types.Block
198 | for receipt := range receipts {
199 | blocknum := receipt.BlockNumber.Uint64()
200 | if _, ok := blockNumbers[blocknum]; !ok {
201 | blockHash := receipt.BlockHash.Hex()
202 | block, err := client.BlockByHash(ctx, common.HexToHash(blockHash))
203 | if err != nil {
204 | log.Fatalf("Error getting block: %v", err)
205 | }
206 | excessDataGas := block.ExcessDataGas()
207 | if excessDataGas == nil {
208 | log.Fatalf("nil excess_blobs in block header. block_hash=%v", blockHash)
209 | }
210 | blockNumbers[blocknum] = true
211 | blocks = append(blocks, block)
212 | }
213 | }
214 | sort.Slice(blocks, func(i, j int) bool {
215 | return blocks[i].Number().Uint64() < blocks[j].Number().Uint64()
216 | })
217 |
218 | prevExcessDataGas := new(big.Int)
219 | parentBlock, err := client.BlockByHash(ctx, blocks[0].ParentHash())
220 | if err != nil {
221 | log.Fatalf("Error getting block: %v", err)
222 | }
223 | if e := parentBlock.ExcessDataGas(); e != nil {
224 | prevExcessDataGas.Set(e)
225 | }
226 |
227 | for _, block := range blocks {
228 | // Assuming each transaction contains a single blob
229 | expected := misc.CalcExcessDataGas(prevExcessDataGas, len(block.Transactions()))
230 | if expected.Cmp(block.ExcessDataGas()) != 0 {
231 | log.Fatalf("unexpected excess_data_gas field in header. expected %v. got %v", expected, block.ExcessDataGas())
232 | }
233 | prevExcessDataGas = expected
234 | }
235 | }
236 |
237 | func FindBlocksWithBlobs(ctx context.Context, client *beacon.Client, startSlot consensustypes.Slot) []*util.Block {
238 | slot := startSlot
239 | endSlot := util.GetHeadSlot(ctx, client)
240 |
241 | var blocks []*util.Block
242 | for {
243 | if slot == endSlot {
244 | break
245 | }
246 |
247 | block, err := util.GetBlock(ctx, client, beacon.IdFromSlot(slot))
248 | if err != nil {
249 | log.Fatalf("Failed to GetBlock: %v", err)
250 | }
251 |
252 | if len(block.Data.Message.Body.BlobKzgCommitments) != 0 {
253 | blocks = append(blocks, block)
254 | }
255 |
256 | slot = slot.Add(1)
257 | }
258 |
259 | if len(blocks) == 0 {
260 | log.Fatalf("Unable to find beacon block containing blobs")
261 | }
262 | return blocks
263 | }
264 |
--------------------------------------------------------------------------------
/shared/genesis.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "chainId": 1,
4 | "homesteadBlock": 0,
5 | "eip150Block": 0,
6 | "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
7 | "eip155Block": 0,
8 | "eip158Block": 0,
9 | "byzantiumBlock": 0,
10 | "constantinopleBlock": 0,
11 | "petersburgBlock": 0,
12 | "istanbulBlock": 0,
13 | "muirGlacierBlock": 0,
14 | "berlinBlock": 0,
15 | "londonBlock": 0,
16 | "shanghaiTime": SHANGHAI_TIME,
17 | "shardingForkTime": SHARDING_FORK_TIME,
18 | "terminalTotalDifficulty": 0,
19 | "terminalTotalDifficultyPassed": true
20 | },
21 | "nonce": "0x1234",
22 | "timestamp": "GENESIS_TIME",
23 | "gasLimit": "0x400000",
24 | "difficulty": "0x01",
25 | "coinbase": "0x0000000000000000000000000000000000000000",
26 | "number": "0x0",
27 | "gasUsed": "0x0",
28 | "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
29 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
30 | "baseFeePerGas": "0x7",
31 | "alloc": {
32 | "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
33 | "balance": "0x6d6172697573766477000000"
34 | },
35 | "0x8A04d14125D0FDCDc742F4A05C051De07232EDa4": {
36 | "code": "0x60806040526004361061003f5760003560e01c806301ffc9a714610044578063228951181461008c578063621fd130146101a2578063c5f2892f1461022c575b600080fd5b34801561005057600080fd5b506100786004803603602081101561006757600080fd5b50356001600160e01b031916610253565b604080519115158252519081900360200190f35b6101a0600480360360808110156100a257600080fd5b8101906020810181356401000000008111156100bd57600080fd5b8201836020820111156100cf57600080fd5b803590602001918460018302840111640100000000831117156100f157600080fd5b91939092909160208101903564010000000081111561010f57600080fd5b82018360208201111561012157600080fd5b8035906020019184600183028401116401000000008311171561014357600080fd5b91939092909160208101903564010000000081111561016157600080fd5b82018360208201111561017357600080fd5b8035906020019184600183028401116401000000008311171561019557600080fd5b91935091503561028a565b005b3480156101ae57600080fd5b506101b7610ce6565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101f15781810151838201526020016101d9565b50505050905090810190601f16801561021e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561023857600080fd5b50610241610cf8565b60408051918252519081900360200190f35b60006001600160e01b031982166301ffc9a760e01b148061028457506001600160e01b03198216638564090760e01b145b92915050565b603086146102c95760405162461bcd60e51b81526004018080602001828103825260268152602001806112516026913960400191505060405180910390fd5b602084146103085760405162461bcd60e51b81526004018080602001828103825260368152602001806111e86036913960400191505060405180910390fd5b606082146103475760405162461bcd60e51b81526004018080602001828103825260298152602001806112c46029913960400191505060405180910390fd5b670de0b6b3a764000034101561038e5760405162461bcd60e51b815260040180806020018281038252602681526020018061129e6026913960400191505060405180910390fd5b633b9aca003406156103d15760405162461bcd60e51b815260040180806020018281038252603381526020018061121e6033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff81111561041f5760405162461bcd60e51b81526004018080602001828103825260278152602001806112776027913960400191505060405180910390fd5b606061042a82610fc6565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a61045f602054610fc6565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f01601f191690910187810386528c815260200190508c8c808284376000838201819052601f909101601f191690920188810386528c5181528c51602091820193918e019250908190849084905b838110156104f65781810151838201526020016104de565b50505050905090810190601f1680156105235780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f909101601f19169092018881038452895181528951602091820193918b019250908190849084905b8381101561057f578181015183820152602001610567565b50505050905090810190601f1680156105ac5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284376fffffffffffffffffffffffffffffffff199094169190930190815260408051600f19818403018152601090920190819052815191955093508392506020850191508083835b602083106106415780518252601f199092019160209182019101610622565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610680573d6000803e3d6000fd5b5050506040513d602081101561069557600080fd5b5051905060006002806106ab6040848a8c61114a565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106107015780518252601f1990920191602091820191016106e2565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610740573d6000803e3d6000fd5b5050506040513d602081101561075557600080fd5b50516002610766896040818d61114a565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106107c15780518252601f1990920191602091820191016107a2565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610800573d6000803e3d6000fd5b5050506040513d602081101561081557600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b6020831061086b5780518252601f19909201916020918201910161084c565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa1580156108aa573d6000803e3d6000fd5b5050506040513d60208110156108bf57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b6020831061092e5780518252601f19909201916020918201910161090f565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa15801561096d573d6000803e3d6000fd5b5050506040513d602081101561098257600080fd5b50516040518651600291889160009188916020918201918291908601908083835b602083106109c25780518252601f1990920191602091820191016109a3565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610a495780518252601f199092019160209182019101610a2a565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610a88573d6000803e3d6000fd5b5050506040513d6020811015610a9d57600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610af35780518252601f199092019160209182019101610ad4565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610b32573d6000803e3d6000fd5b5050506040513d6020811015610b4757600080fd5b50519050858114610b895760405162461bcd60e51b81526004018080602001828103825260548152602001806111946054913960600191505060405180910390fd5b60205463ffffffff11610bcd5760405162461bcd60e51b81526004018080602001828103825260218152602001806111736021913960400191505060405180910390fd5b602080546001019081905560005b6020811015610cda578160011660011415610c0d578260008260208110610bfe57fe5b015550610cdd95505050505050565b600260008260208110610c1c57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610c745780518252601f199092019160209182019101610c55565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610cb3573d6000803e3d6000fd5b5050506040513d6020811015610cc857600080fd5b50519250600282049150600101610bdb565b50fe5b50505050505050565b6060610cf3602054610fc6565b905090565b6020546000908190815b6020811015610ea9578160011660011415610ddb57600260008260208110610d2657fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610d7e5780518252601f199092019160209182019101610d5f565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610dbd573d6000803e3d6000fd5b5050506040513d6020811015610dd257600080fd5b50519250610e9b565b60028360218360208110610deb57fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610e425780518252601f199092019160209182019101610e23565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610e81573d6000803e3d6000fd5b5050506040513d6020811015610e9657600080fd5b505192505b600282049150600101610d02565b50600282610eb8602054610fc6565b600060401b6040516020018084815260200183805190602001908083835b60208310610ef55780518252601f199092019160209182019101610ed6565b51815160209384036101000a600019018019909216911617905267ffffffffffffffff199590951692019182525060408051808303600719018152601890920190819052815191955093508392850191508083835b60208310610f695780518252601f199092019160209182019101610f4a565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610fa8573d6000803e3d6000fd5b5050506040513d6020811015610fbd57600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b8260008151811061100057fe5b60200101906001600160f81b031916908160001a9053508060061a60f81b8260018151811061102b57fe5b60200101906001600160f81b031916908160001a9053508060051a60f81b8260028151811061105657fe5b60200101906001600160f81b031916908160001a9053508060041a60f81b8260038151811061108157fe5b60200101906001600160f81b031916908160001a9053508060031a60f81b826004815181106110ac57fe5b60200101906001600160f81b031916908160001a9053508060021a60f81b826005815181106110d757fe5b60200101906001600160f81b031916908160001a9053508060011a60f81b8260068151811061110257fe5b60200101906001600160f81b031916908160001a9053508060001a60f81b8260078151811061112d57fe5b60200101906001600160f81b031916908160001a90535050919050565b60008085851115611159578182fd5b83861115611165578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a164736f6c634300060b000a",
37 | "balance": "0x0"
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/Inphi/eip4844-interop
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/ethereum/go-ethereum v1.10.26
7 | github.com/holiman/uint256 v1.2.1
8 | github.com/libp2p/go-libp2p v0.24.0
9 | github.com/libp2p/go-libp2p-core v0.17.0
10 | github.com/multiformats/go-multiaddr v0.8.0
11 | github.com/pkg/errors v0.9.1
12 | github.com/protolambda/ztyp v0.2.1
13 | github.com/prysmaticlabs/fastssz v0.0.0-20221107182844-78142813af44
14 | github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7
15 | github.com/prysmaticlabs/prysm/v3 v3.2.0-rc.0.0.20221215090238-7866e8a1967f
16 | golang.org/x/sync v0.1.0
17 | gopkg.in/yaml.v2 v2.4.0
18 | )
19 |
20 | require (
21 | contrib.go.opencensus.io/exporter/jaeger v0.2.1 // indirect
22 | github.com/BurntSushi/toml v1.2.1 // indirect
23 | github.com/VictoriaMetrics/fastcache v1.12.0 // indirect
24 | github.com/allegro/bigcache v1.2.1 // indirect
25 | github.com/aristanetworks/goarista v0.0.0-20200805130819-fd197cf57d96 // indirect
26 | github.com/benbjohnson/clock v1.3.0 // indirect
27 | github.com/beorn7/perks v1.0.1 // indirect
28 | github.com/btcsuite/btcd v0.23.1 // indirect
29 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
30 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
31 | github.com/cespare/xxhash v1.1.0 // indirect
32 | github.com/cespare/xxhash/v2 v2.2.0 // indirect
33 | github.com/containerd/cgroups v1.0.4 // indirect
34 | github.com/coreos/go-systemd/v22 v22.5.0 // indirect
35 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
36 | github.com/davecgh/go-spew v1.1.1 // indirect
37 | github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
38 | github.com/deckarep/golang-set/v2 v2.1.0 // indirect
39 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
40 | github.com/dgraph-io/ristretto v0.0.4-0.20210318174700-74754f61e018 // indirect
41 | github.com/docker/go-units v0.5.0 // indirect
42 | github.com/dustin/go-humanize v1.0.0 // indirect
43 | github.com/elastic/gosigar v0.14.2 // indirect
44 | github.com/fjl/memsize v0.0.1 // indirect
45 | github.com/flynn/noise v1.0.0 // indirect
46 | github.com/francoispqt/gojay v1.2.13 // indirect
47 | github.com/fsnotify/fsnotify v1.6.0 // indirect
48 | github.com/go-logr/logr v1.2.3 // indirect
49 | github.com/go-ole/go-ole v1.2.6 // indirect
50 | github.com/go-stack/stack v1.8.1 // indirect
51 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
52 | github.com/godbus/dbus/v5 v5.1.0 // indirect
53 | github.com/gogo/protobuf v1.3.2 // indirect
54 | github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
55 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
56 | github.com/golang/mock v1.6.0 // indirect
57 | github.com/golang/protobuf v1.5.2 // indirect
58 | github.com/golang/snappy v0.0.4 // indirect
59 | github.com/google/go-cmp v0.5.9 // indirect
60 | github.com/google/gofuzz v1.2.0 // indirect
61 | github.com/google/gopacket v1.1.19 // indirect
62 | github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect
63 | github.com/google/uuid v1.3.0 // indirect
64 | github.com/gorilla/mux v1.8.0 // indirect
65 | github.com/gorilla/websocket v1.5.0 // indirect
66 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1 // indirect
67 | github.com/hashicorp/go-bexpr v0.1.11 // indirect
68 | github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
69 | github.com/herumi/bls-eth-go-binary v1.28.1 // indirect
70 | github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e // indirect
71 | github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
72 | github.com/huin/goupnp v1.0.3 // indirect
73 | github.com/ipfs/go-cid v0.3.2 // indirect
74 | github.com/ipfs/go-log v1.0.5 // indirect
75 | github.com/ipfs/go-log/v2 v2.5.1 // indirect
76 | github.com/jackpal/go-nat-pmp v1.0.2 // indirect
77 | github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
78 | github.com/json-iterator/go v1.1.12 // indirect
79 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
80 | github.com/kilic/bls12-381 v0.1.1-0.20220929213557-ca162e8a70f4 // indirect
81 | github.com/klauspost/compress v1.15.12 // indirect
82 | github.com/klauspost/cpuid/v2 v2.2.1 // indirect
83 | github.com/koron/go-ssdp v0.0.3 // indirect
84 | github.com/kr/pretty v0.3.0 // indirect
85 | github.com/kr/text v0.2.0 // indirect
86 | github.com/libp2p/go-buffer-pool v0.1.0 // indirect
87 | github.com/libp2p/go-cidranger v1.1.0 // indirect
88 | github.com/libp2p/go-flow-metrics v0.1.0 // indirect
89 | github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect
90 | github.com/libp2p/go-libp2p-pubsub v0.8.0 // indirect
91 | github.com/libp2p/go-mplex v0.7.0 // indirect
92 | github.com/libp2p/go-msgio v0.2.0 // indirect
93 | github.com/libp2p/go-nat v0.1.0 // indirect
94 | github.com/libp2p/go-netroute v0.2.1 // indirect
95 | github.com/libp2p/go-openssl v0.1.0 // indirect
96 | github.com/libp2p/go-reuseport v0.2.0 // indirect
97 | github.com/libp2p/go-yamux/v4 v4.0.0 // indirect
98 | github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
99 | github.com/lucas-clemente/quic-go v0.31.0 // indirect
100 | github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
101 | github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
102 | github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
103 | github.com/mattn/go-colorable v0.1.12 // indirect
104 | github.com/mattn/go-isatty v0.0.16 // indirect
105 | github.com/mattn/go-pointer v0.0.1 // indirect
106 | github.com/mattn/go-runewidth v0.0.14 // indirect
107 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
108 | github.com/miekg/dns v1.1.50 // indirect
109 | github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
110 | github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
111 | github.com/minio/highwayhash v1.0.1 // indirect
112 | github.com/minio/sha256-simd v1.0.0 // indirect
113 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
114 | github.com/mitchellh/mapstructure v1.4.3 // indirect
115 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
116 | github.com/modern-go/reflect2 v1.0.2 // indirect
117 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
118 | github.com/mr-tron/base58 v1.2.0 // indirect
119 | github.com/multiformats/go-base32 v0.1.0 // indirect
120 | github.com/multiformats/go-base36 v0.2.0 // indirect
121 | github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
122 | github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
123 | github.com/multiformats/go-multibase v0.1.1 // indirect
124 | github.com/multiformats/go-multicodec v0.7.0 // indirect
125 | github.com/multiformats/go-multihash v0.2.1 // indirect
126 | github.com/multiformats/go-multistream v0.3.3 // indirect
127 | github.com/multiformats/go-varint v0.0.7 // indirect
128 | github.com/olekukonko/tablewriter v0.0.5 // indirect
129 | github.com/onsi/ginkgo/v2 v2.5.1 // indirect
130 | github.com/opencontainers/runtime-spec v1.0.2 // indirect
131 | github.com/opentracing/opentracing-go v1.2.0 // indirect
132 | github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
133 | github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
134 | github.com/prometheus/client_golang v1.14.0 // indirect
135 | github.com/prometheus/client_model v0.3.0 // indirect
136 | github.com/prometheus/common v0.37.0 // indirect
137 | github.com/prometheus/procfs v0.8.0 // indirect
138 | github.com/prometheus/prom2json v1.3.0 // indirect
139 | github.com/prometheus/tsdb v0.10.0 // indirect
140 | github.com/protolambda/go-kzg v0.0.0-20221129234330-612948a21fb0 // indirect
141 | github.com/prysmaticlabs/gohashtree v0.0.2-alpha // indirect
142 | github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c // indirect
143 | github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc // indirect
144 | github.com/raulk/go-watchdog v1.3.0 // indirect
145 | github.com/rivo/uniseg v0.4.3 // indirect
146 | github.com/rogpeppe/go-internal v1.8.0 // indirect
147 | github.com/rs/cors v1.8.2 // indirect
148 | github.com/russross/blackfriday/v2 v2.1.0 // indirect
149 | github.com/schollz/progressbar/v3 v3.3.4 // indirect
150 | github.com/shirou/gopsutil v3.21.11+incompatible // indirect
151 | github.com/sirupsen/logrus v1.9.0 // indirect
152 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
153 | github.com/spaolacci/murmur3 v1.1.0 // indirect
154 | github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344 // indirect
155 | github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
156 | github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect
157 | github.com/tklauser/go-sysconf v0.3.11 // indirect
158 | github.com/tklauser/numcpus v0.6.0 // indirect
159 | github.com/trailofbits/go-mutexasserts v0.0.0-20200708152505-19999e7d3cef // indirect
160 | github.com/uber/jaeger-client-go v2.25.0+incompatible // indirect
161 | github.com/urfave/cli/v2 v2.23.5 // indirect
162 | github.com/wealdtech/go-bytesutil v1.1.1 // indirect
163 | github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect
164 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
165 | github.com/yusufpapurcu/wmi v1.2.2 // indirect
166 | go.etcd.io/bbolt v1.3.5 // indirect
167 | go.opencensus.io v0.24.0 // indirect
168 | go.uber.org/atomic v1.10.0 // indirect
169 | go.uber.org/dig v1.15.0 // indirect
170 | go.uber.org/fx v1.18.2 // indirect
171 | go.uber.org/multierr v1.8.0 // indirect
172 | go.uber.org/zap v1.24.0 // indirect
173 | golang.org/x/crypto v0.3.0 // indirect
174 | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
175 | golang.org/x/mod v0.7.0 // indirect
176 | golang.org/x/net v0.3.0 // indirect
177 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
178 | golang.org/x/sys v0.3.0 // indirect
179 | golang.org/x/term v0.3.0 // indirect
180 | golang.org/x/text v0.5.0 // indirect
181 | golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
182 | golang.org/x/tools v0.3.0 // indirect
183 | google.golang.org/api v0.34.0 // indirect
184 | google.golang.org/appengine v1.6.7 // indirect
185 | google.golang.org/genproto v0.0.0-20210426193834-eac7f76ac494 // indirect
186 | google.golang.org/grpc v1.40.0 // indirect
187 | google.golang.org/protobuf v1.28.1 // indirect
188 | gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
189 | gopkg.in/inf.v0 v0.9.1 // indirect
190 | gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
191 | gopkg.in/yaml.v3 v3.0.1 // indirect
192 | k8s.io/apimachinery v0.18.3 // indirect
193 | k8s.io/client-go v0.18.3 // indirect
194 | k8s.io/klog v1.0.0 // indirect
195 | k8s.io/klog/v2 v2.80.0 // indirect
196 | k8s.io/utils v0.0.0-20200520001619-278ece378a50 // indirect
197 | lukechampine.com/blake3 v1.1.7 // indirect
198 | sigs.k8s.io/structured-merge-diff/v3 v3.0.0 // indirect
199 | sigs.k8s.io/yaml v1.2.0 // indirect
200 | )
201 |
202 | replace github.com/ethereum/go-ethereum => github.com/mdehoog/go-ethereum v1.10.19-0.20230108160323-2b556dbb6624
203 |
204 | // replace github.com/prysmaticlabs/prysm/v3 => ./prysm/prysm
205 |
206 | // See https://github.com/prysmaticlabs/grpc-gateway/issues/2
207 | replace github.com/grpc-ecosystem/grpc-gateway/v2 => github.com/prysmaticlabs/grpc-gateway/v2 v2.3.1-0.20220721162526-0d1c40b5f064
208 |
--------------------------------------------------------------------------------
/tests/util/download.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "errors"
7 | "fmt"
8 | "io"
9 | "log"
10 | "reflect"
11 | "runtime/debug"
12 | "strings"
13 | "time"
14 |
15 | "github.com/Inphi/eip4844-interop/shared"
16 | "github.com/libp2p/go-libp2p"
17 | "github.com/libp2p/go-libp2p/core/host"
18 | "github.com/libp2p/go-libp2p/core/network"
19 | "github.com/libp2p/go-libp2p/core/peer"
20 | "github.com/libp2p/go-libp2p/core/protocol"
21 | "github.com/libp2p/go-libp2p/p2p/protocol/identify"
22 | "github.com/libp2p/go-libp2p/p2p/transport/tcp"
23 | ma "github.com/multiformats/go-multiaddr"
24 | ssz "github.com/prysmaticlabs/fastssz"
25 | "github.com/prysmaticlabs/go-bitfield"
26 | "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p"
27 | "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/encoder"
28 | p2ptypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/types"
29 | "github.com/prysmaticlabs/prysm/v3/beacon-chain/sync"
30 | consensustypes "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
31 | "github.com/prysmaticlabs/prysm/v3/consensus-types/wrapper"
32 | ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
33 | "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1/metadata"
34 | )
35 |
36 | var responseCodeSuccess = byte(0x00)
37 |
38 | func SendBlobsSidecarsByRangeRequest(ctx context.Context, h host.Host, encoding encoder.NetworkEncoding, pid peer.ID, req *ethpb.BlobsSidecarsByRangeRequest) ([]*ethpb.BlobsSidecar, error) {
39 | topic := fmt.Sprintf("%s%s", p2p.RPCBlobsSidecarsByRangeTopicV1, encoding.ProtocolSuffix())
40 |
41 | stream, err := h.NewStream(ctx, pid, protocol.ID(topic))
42 | if err != nil {
43 | return nil, err
44 | }
45 | defer func() {
46 | _ = stream.Close()
47 | }()
48 |
49 | if _, err := encoding.EncodeWithMaxLength(stream, req); err != nil {
50 | _ = stream.Reset()
51 | return nil, err
52 | }
53 |
54 | if err := stream.CloseWrite(); err != nil {
55 | _ = stream.Reset()
56 | return nil, err
57 | }
58 |
59 | var blobsSidecars []*ethpb.BlobsSidecar
60 | for {
61 | isFirstChunk := len(blobsSidecars) == 0
62 | blobs, err := readChunkedBlobsSidecar(stream, encoding, isFirstChunk)
63 | if errors.Is(err, io.EOF) {
64 | break
65 | }
66 | if err != nil {
67 | return nil, err
68 | }
69 | blobsSidecars = append(blobsSidecars, blobs)
70 | }
71 | return blobsSidecars, nil
72 | }
73 |
74 | func readChunkedBlobsSidecar(stream network.Stream, encoding encoder.NetworkEncoding, isFirstChunk bool) (*ethpb.BlobsSidecar, error) {
75 | var (
76 | code uint8
77 | errMsg string
78 | err error
79 | )
80 | if isFirstChunk {
81 | code, errMsg, err = sync.ReadStatusCode(stream, encoding)
82 | } else {
83 | sync.SetStreamReadDeadline(stream, time.Second*10)
84 | code, errMsg, err = readStatusCodeNoDeadline(stream, encoding)
85 | }
86 | if err != nil {
87 | return nil, err
88 | }
89 | if code != 0 {
90 | return nil, errors.New(errMsg)
91 | }
92 | // ignored: we assume we got the correct context
93 | b := make([]byte, 4)
94 | if _, err := stream.Read(b); err != nil {
95 | return nil, err
96 | }
97 | sidecar := new(ethpb.BlobsSidecar)
98 | err = encoding.DecodeWithMaxLength(stream, sidecar)
99 | return sidecar, err
100 | }
101 |
102 | func readStatusCodeNoDeadline(stream network.Stream, encoding encoder.NetworkEncoding) (uint8, string, error) {
103 | b := make([]byte, 1)
104 | _, err := stream.Read(b)
105 | if err != nil {
106 | return 0, "", err
107 | }
108 | if b[0] == responseCodeSuccess {
109 | return 0, "", nil
110 | }
111 | msg := &p2ptypes.ErrorMessage{}
112 | if err := encoding.DecodeWithMaxLength(stream, msg); err != nil {
113 | return 0, "", err
114 | }
115 | return b[0], string(*msg), nil
116 | }
117 |
118 | // Using p2p RPC
119 | func DownloadBlobs(ctx context.Context, startSlot consensustypes.Slot, count uint64, beaconMA string) []byte {
120 | log.Print("downloading blobs...")
121 |
122 | req := ðpb.BlobsSidecarsByRangeRequest{
123 | StartSlot: startSlot,
124 | Count: count,
125 | }
126 |
127 | h, err := libp2p.New(libp2p.Transport(tcp.NewTCPTransport))
128 | if err != nil {
129 | log.Fatalf("failed to create libp2p context: %v", err)
130 | }
131 | defer func() {
132 | _ = h.Close()
133 | }()
134 | h.RemoveStreamHandler(identify.IDDelta)
135 | // setup enough handlers so lighthouse thinks it's dealing with a beacon peer
136 | setHandler(h, p2p.RPCPingTopicV1, pingHandler)
137 | setHandler(h, p2p.RPCGoodByeTopicV1, pingHandler)
138 | setHandler(h, p2p.RPCMetaDataTopicV1, pingHandler)
139 | setHandler(h, p2p.RPCMetaDataTopicV2, pingHandler)
140 |
141 | nilHandler := func(ctx context.Context, i interface{}, stream network.Stream) error {
142 | log.Printf("received request for %s", stream.Protocol())
143 | return nil
144 | }
145 | setHandler(h, p2p.RPCBlocksByRangeTopicV1, nilHandler)
146 | setHandler(h, p2p.RPCBlocksByRangeTopicV2, nilHandler)
147 | setHandler(h, p2p.RPCBlobsSidecarsByRangeTopicV1, nilHandler)
148 |
149 | maddr, err := ma.NewMultiaddr(beaconMA)
150 | if err != nil {
151 | log.Fatalf("failed to get multiaddr: %v", err)
152 | }
153 | addrInfo, err := peer.AddrInfoFromP2pAddr(maddr)
154 | if err != nil {
155 | log.Fatalf("failed to get addr info: %v", err)
156 | }
157 |
158 | err = h.Connect(ctx, *addrInfo)
159 | if err != nil {
160 | log.Fatalf("libp2p host connect: %v", err)
161 | }
162 |
163 | sidecars, err := SendBlobsSidecarsByRangeRequest(ctx, h, encoder.SszNetworkEncoder{}, addrInfo.ID, req)
164 | if err != nil {
165 | log.Fatalf("failed to send blobs p2p request: %v", err)
166 | }
167 |
168 | anyBlobs := false
169 | blobsBuffer := new(bytes.Buffer)
170 | for _, sidecar := range sidecars {
171 | if sidecar.Blobs == nil || len(sidecar.Blobs) == 0 {
172 | continue
173 | }
174 | anyBlobs = true
175 | for _, blob := range sidecar.Blobs {
176 | data := shared.DecodeFlatBlob(blob.Data)
177 | _, _ = blobsBuffer.Write(data)
178 | }
179 |
180 | // stop after the first sidecar with blobs:
181 | break
182 | }
183 | if !anyBlobs {
184 | log.Fatalf("No blobs found in requested slots, sidecar count: %d", len(sidecars))
185 | }
186 |
187 | return blobsBuffer.Bytes()
188 | }
189 |
190 | func getMultiaddr(ctx context.Context, h host.Host, addr string) (ma.Multiaddr, error) {
191 | multiaddr, err := ma.NewMultiaddr(addr)
192 | if err != nil {
193 | return nil, err
194 | }
195 | _, id := peer.SplitAddr(multiaddr)
196 | if id != "" {
197 | return multiaddr, nil
198 | }
199 | // peer ID wasn't provided, look it up
200 | id, err = retrievePeerID(ctx, h, addr)
201 | if err != nil {
202 | return nil, err
203 | }
204 | return ma.NewMultiaddr(fmt.Sprintf("%s/p2p/%s", addr, string(id)))
205 | }
206 |
207 | // Helper for retrieving the peer ID from a security error... obviously don't use this in production!
208 | // See https://github.com/libp2p/go-libp2p-noise/blob/v0.3.0/handshake.go#L250
209 | func retrievePeerID(ctx context.Context, h host.Host, addr string) (peer.ID, error) {
210 | incorrectPeerID := "16Uiu2HAmSifdT5QutTsaET8xqjWAMPp4obrQv7LN79f2RMmBe3nY"
211 | addrInfo, err := peer.AddrInfoFromString(fmt.Sprintf("%s/p2p/%s", addr, incorrectPeerID))
212 | if err != nil {
213 | return "", err
214 | }
215 | err = h.Connect(ctx, *addrInfo)
216 | if err == nil {
217 | return "", errors.New("unexpected successful connection")
218 | }
219 | if strings.Contains(err.Error(), "but remote key matches") {
220 | split := strings.Split(err.Error(), " ")
221 | return peer.ID(split[len(split)-1]), nil
222 | }
223 | return "", err
224 | }
225 |
226 | type rpcHandler func(context.Context, interface{}, network.Stream) error
227 |
228 | // adapted from prysm's handler router
229 | func setHandler(h host.Host, baseTopic string, handler rpcHandler) {
230 | encoding := &encoder.SszNetworkEncoder{}
231 | topic := baseTopic + encoding.ProtocolSuffix()
232 | h.SetStreamHandler(protocol.ID(topic), func(stream network.Stream) {
233 | defer func() {
234 | if r := recover(); r != nil {
235 | log.Printf("Panic occurred: %v", r)
236 | log.Printf("%s", debug.Stack())
237 | }
238 | }()
239 |
240 | // Resetting after closing is a no-op so defer a reset in case something goes wrong.
241 | // It's up to the handler to Close the stream (send an EOF) if
242 | // it successfully writes a response. We don't blindly call
243 | // Close here because we may have only written a partial
244 | // response.
245 | defer func() {
246 | _err := stream.Reset()
247 | _ = _err
248 | }()
249 |
250 | base, ok := p2p.RPCTopicMappings[baseTopic]
251 | if !ok {
252 | log.Printf("ERROR: Could not retrieve base message for topic %s", baseTopic)
253 | return
254 | }
255 | bb := base
256 | t := reflect.TypeOf(base)
257 | // Copy Base
258 | base = reflect.New(t)
259 |
260 | if baseTopic == p2p.RPCMetaDataTopicV1 || baseTopic == p2p.RPCMetaDataTopicV2 {
261 | if err := metadataHandler(context.Background(), base, stream); err != nil {
262 | if err != p2ptypes.ErrWrongForkDigestVersion {
263 | log.Printf("ERROR: Could not handle p2p RPC: %v", err)
264 | }
265 | }
266 | return
267 | }
268 |
269 | // Given we have an input argument that can be pointer or the actual object, this gives us
270 | // a way to check for its reflect.Kind and based on the result, we can decode
271 | // accordingly.
272 | if t.Kind() == reflect.Ptr {
273 | msg, ok := reflect.New(t.Elem()).Interface().(ssz.Unmarshaler)
274 | if !ok {
275 | log.Printf("ERROR: message of %T ptr does not support marshaller interface. topic=%s", bb, baseTopic)
276 | return
277 | }
278 | if err := encoding.DecodeWithMaxLength(stream, msg); err != nil {
279 | log.Printf("ERROR: could not decode stream message: %v", err)
280 | return
281 | }
282 | if err := handler(context.Background(), msg, stream); err != nil {
283 | if err != p2ptypes.ErrWrongForkDigestVersion {
284 | log.Printf("ERROR: Could not handle p2p RPC: %v", err)
285 | }
286 | }
287 | } else {
288 | nTyp := reflect.New(t)
289 | msg, ok := nTyp.Interface().(ssz.Unmarshaler)
290 | if !ok {
291 | log.Printf("ERROR: message of %T does not support marshaller interface", msg)
292 | return
293 | }
294 | if err := handler(context.Background(), msg, stream); err != nil {
295 | if err != p2ptypes.ErrWrongForkDigestVersion {
296 | log.Printf("ERROR: Could not handle p2p RPC: %v", err)
297 | }
298 | }
299 | }
300 | })
301 | }
302 |
303 | func dummyMetadata() metadata.Metadata {
304 | metaData := ðpb.MetaDataV1{
305 | SeqNumber: 0,
306 | Attnets: bitfield.NewBitvector64(),
307 | Syncnets: bitfield.Bitvector4{byte(0x00)},
308 | }
309 | return wrapper.WrappedMetadataV1(metaData)
310 | }
311 |
312 | // pingHandler reads the incoming ping rpc message from the peer.
313 | func pingHandler(_ context.Context, _ interface{}, stream network.Stream) error {
314 | encoding := &encoder.SszNetworkEncoder{}
315 | defer closeStream(stream)
316 | if _, err := stream.Write([]byte{responseCodeSuccess}); err != nil {
317 | return err
318 | }
319 | m := dummyMetadata()
320 | sq := consensustypes.SSZUint64(m.SequenceNumber())
321 | if _, err := encoding.EncodeWithMaxLength(stream, &sq); err != nil {
322 | return fmt.Errorf("%w: pingHandler stream write", err)
323 | }
324 | return nil
325 | }
326 |
327 | // metadataHandler spoofs a valid looking metadata message
328 | func metadataHandler(_ context.Context, _ interface{}, stream network.Stream) error {
329 | encoding := &encoder.SszNetworkEncoder{}
330 | defer closeStream(stream)
331 | if _, err := stream.Write([]byte{responseCodeSuccess}); err != nil {
332 | return err
333 | }
334 |
335 | // write a dummy metadata message to satify the client handshake
336 | m := dummyMetadata()
337 | if _, err := encoding.EncodeWithMaxLength(stream, m); err != nil {
338 | return fmt.Errorf("%w: metadata stream write", err)
339 | }
340 | return nil
341 | }
342 |
343 | func closeStream(stream network.Stream) {
344 | if err := stream.Close(); err != nil {
345 | log.Println(err)
346 | }
347 | }
348 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.4'
2 |
3 | volumes:
4 | lighthouse_data:
5 | prysm_data:
6 | lodestar_data:
7 | config_data:
8 | teku_data:
9 |
10 | x-geth: &geth
11 | image: inphi/geth:eip4844-devnet4
12 | depends_on:
13 | genesis-generator:
14 | condition: service_completed_successfully
15 | entrypoint: geth.sh
16 | healthcheck:
17 | test: ["CMD", "curl", "--fail", "localhost:8545"]
18 | interval: 2s
19 | timeout: 2s
20 | retries: 40
21 | start_period: 1s
22 | volumes:
23 | - 'config_data:/config_data'
24 | - type: bind
25 | source: ./geth/geth.sh
26 | target: /usr/local/bin/geth.sh
27 |
28 | x-nethermind: &nethermind
29 | image: nethermindeth/nethermind:eip-4844-v4-workaround
30 | depends_on:
31 | genesis-generator:
32 | condition: service_completed_successfully
33 | entrypoint: 'nethermind.sh'
34 | healthcheck:
35 | test: ["CMD", "curl", "--fail", "localhost:8545"]
36 | interval: 2s
37 | timeout: 2s
38 | retries: 40
39 | start_period: 1s
40 | volumes:
41 | - 'config_data:/config_data'
42 | - type: bind
43 | source: ./nethermind/nethermind.sh
44 | target: /usr/local/bin/nethermind.sh
45 |
46 | x-besu: &besu
47 | depends_on:
48 | genesis-generator:
49 | condition: service_completed_successfully
50 | entrypoint: 'besu.sh'
51 | build:
52 | context: .
53 | dockerfile: besu/Dockerfile.besu
54 | healthcheck:
55 | test: ["CMD", "curl", "--fail", "localhost:8545"]
56 | interval: 2s
57 | timeout: 2s
58 | retries: 40
59 | start_period: 1s
60 | volumes:
61 | - 'config_data:/config_data'
62 | - type: bind
63 | source: ./besu/besu.sh
64 | target: /usr/local/bin/besu.sh
65 | - type: bind
66 | source: ./besu/log4j2.xml
67 | target: /opt/besu/log4j2.xml
68 |
69 | x-prysm-beacon-node-env: &prysm-bn-env
70 | TRACING_ENDPOINT: http://jaeger-tracing:14268/api/traces
71 | PROCESS_NAME: beacon-node
72 | VERBOSITY: debug
73 | P2P_TCP_PORT: 13000
74 | MIN_SYNC_PEERS: 0
75 |
76 | x-prysm-beacon-node: &prysm-bn
77 | depends_on:
78 | bootnode:
79 | condition: service_started
80 | jaeger-tracing:
81 | condition: service_started
82 | genesis-generator:
83 | condition: service_completed_successfully
84 | build:
85 | context: ./prysm
86 | dockerfile: Dockerfile.prysm
87 | healthcheck:
88 | test: ["CMD", "curl", "--fail", "localhost:3500/eth/v1/node/health"]
89 | interval: 2s
90 | timeout: 2s
91 | retries: 40
92 | start_period: 1s
93 | entrypoint: run_beacon_node.sh
94 | volumes:
95 | - 'config_data:/config_data'
96 | - type: bind
97 | source: ./prysm/run_beacon_node.sh
98 | target: /usr/local/bin/run_beacon_node.sh
99 |
100 | x-lighthouse-beacon-node: &lighthouse-bn
101 | image: inphi/lighthouse:eip4844
102 | entrypoint: ["run_beacon_node.sh", "-d", "debug", "/data/node_1", "http://geth-1:8551"]
103 | environment:
104 | - P2P_PORT=9000
105 | ports:
106 | - "5052:5052"
107 | - "8000:8000"
108 | - "9000:9000"
109 | volumes:
110 | - lighthouse_data:/data
111 | - config_data:/config_data
112 | - type: bind
113 | source: ./lighthouse/run_beacon_node.sh
114 | target: /usr/local/bin/run_beacon_node.sh
115 | - type: bind
116 | source: ./shared/trusted_setup.txt
117 | target: /config/trusted_setup.txt
118 | - type: bind
119 | source: ./shared/jwtsecret
120 | target: /config/jwtsecret
121 |
122 |
123 | services:
124 | genesis-generator:
125 | image: fab10/ethereum-genesis-generator:eip4844
126 | entrypoint: run_genesis_generator.sh
127 | volumes:
128 | - ./shared/run_genesis_generator.sh:/usr/local/bin/run_genesis_generator.sh
129 | - ./shared/genesis-generator-configs:/config
130 | - ./shared/generated-configs:/gen-configs
131 | - lighthouse_data:/lighthouse_data
132 | - prysm_data:/prysm_data
133 | - lodestar_data:/lodestar_data
134 | - teku_data:/teku_data
135 | - config_data:/data
136 |
137 | geth-1:
138 | <<: *geth
139 | ports:
140 | - '8545:8545'
141 | - '8551:8551'
142 |
143 | geth-2:
144 | <<: *geth
145 | ports:
146 | - '8546:8545'
147 | - '8552:8551'
148 |
149 | geth-3:
150 | <<: *geth
151 | ports:
152 | - '8547:8545'
153 | - '8553:8551'
154 |
155 | nethermind-1:
156 | <<: *nethermind
157 | ports:
158 | - '9545:8545'
159 |
160 | nethermind-2:
161 | <<: *nethermind
162 | ports:
163 | - '9546:8545'
164 |
165 | besu-1:
166 | <<: *besu
167 | ports:
168 | - '7551:8551'
169 | - '7545:8545'
170 | - '5005:5005'
171 |
172 | ethereumjs:
173 | image: g11tech/ethereumjs:jan23
174 | depends_on:
175 | genesis-generator:
176 | condition: service_completed_successfully
177 | command: >
178 | --dataDir=/db
179 | --gethGenesis=/config_data/custom_config_data/genesis.json
180 | --rpc
181 | --rpcAddr=0.0.0.0
182 | --rpcCors="*"
183 | --rpcEngine
184 | --rpcEngineAddr=0.0.0.0
185 | --logLevel=debug
186 | --jwt-secret=/config_data/el/jwtsecret
187 | ports:
188 | - '11545:8545'
189 | volumes:
190 | - 'config_data:/config_data'
191 | - type: bind
192 | source: ./lodestar/run_beacon_node.sh
193 | target: /usr/local/bin/run_beacon_node.sh
194 |
195 | # TODO: move bootnode to genesis-generator
196 | bootnode:
197 | depends_on:
198 | genesis-generator:
199 | condition: service_completed_successfully
200 | build:
201 | context: ./lighthouse
202 | dockerfile: Dockerfile.lighthouse
203 | pull_policy: never
204 | command: run_bootnode.sh
205 | volumes:
206 | - lighthouse_data:/data
207 | - config_data:/config_data
208 | - ./shared/genesis-generator-configs:/config
209 | - type: bind
210 | source: ./lighthouse/run_bootnode.sh
211 | target: /usr/local/bin/run_bootnode.sh
212 |
213 | prysm-beacon-node:
214 | <<: *prysm-bn
215 | depends_on:
216 | geth-1:
217 | condition: service_started
218 | bootnode:
219 | condition: service_started
220 | jaeger-tracing:
221 | condition: service_started
222 | genesis-generator:
223 | condition: service_completed_successfully
224 | environment:
225 | <<: *prysm-bn-env
226 | EXECUTION_NODE_URL: http://geth-1:8551
227 | PROCESS_NAME: beacon-node
228 | MIN_SYNC_PEERS: 0
229 | ports:
230 | - '3500:3500'
231 | - '4000:4000'
232 | - '13000:13000'
233 |
234 | prysm-beacon-node-besu-el:
235 | <<: *prysm-bn
236 | depends_on:
237 | besu-1:
238 | condition: service_started
239 | bootnode:
240 | condition: service_started
241 | jaeger-tracing:
242 | condition: service_started
243 | genesis-generator:
244 | condition: service_completed_successfully
245 | environment:
246 | <<: *prysm-bn-env
247 | EXECUTION_NODE_URL: http://besu-1:8551
248 | PROCESS_NAME: beacon-node
249 | MIN_SYNC_PEERS: 0
250 | ports:
251 | - '3500:3500'
252 | - '4000:4000'
253 | - '13000:13000'
254 |
255 | prysm-beacon-node-follower:
256 | <<: *prysm-bn
257 | depends_on:
258 | geth-2:
259 | condition: service_started
260 | bootnode:
261 | condition: service_started
262 | jaeger-tracing:
263 | condition: service_started
264 | genesis-generator:
265 | condition: service_completed_successfully
266 | environment:
267 | <<: *prysm-bn-env
268 | EXECUTION_NODE_URL: http://geth-2:8551
269 | PROCESS_NAME: beacon-node-follower
270 | P2P_TCP_PORT: 13001
271 | MIN_SYNC_PEERS: 1
272 | ports:
273 | - '3501:3500'
274 | - '4001:4000'
275 | - '13001:13001'
276 |
277 | prysm-validator-node:
278 | depends_on:
279 | prysm-beacon-node:
280 | condition: service_started
281 | jaeger-tracing:
282 | condition: service_started
283 | genesis-generator:
284 | condition: service_completed_successfully
285 | build:
286 | context: ./prysm
287 | dockerfile: Dockerfile.prysm
288 | command: >
289 | validator
290 | --accept-terms-of-use
291 | --beacon-rpc-provider prysm-beacon-node:4000
292 | --rpc
293 | --grpc-gateway-host 0.0.0.0
294 | --grpc-gateway-port 7500
295 | --force-clear-db
296 | --chain-config-file=/config_data/custom_config_data/config.yaml
297 | --suggested-fee-recipient 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b
298 | --wallet-password-file=/prysm_data/wallet_pass.txt
299 | --wallet-dir=/prysm_data/wallet
300 | --enable-tracing
301 | --tracing-endpoint http://jaeger-tracing:14268/api/traces
302 | --tracing-process-name validator-node
303 | --verbosity trace
304 | ports:
305 | - '7500:7500'
306 | volumes:
307 | - 'config_data:/config_data'
308 | - 'prysm_data:/prysm_data'
309 |
310 | prysm-validator-node-besu-el:
311 | depends_on:
312 | prysm-beacon-node-besu-el:
313 | condition: service_started
314 | jaeger-tracing:
315 | condition: service_started
316 | genesis-generator:
317 | condition: service_completed_successfully
318 | build:
319 | context: ./prysm
320 | dockerfile: Dockerfile.prysm
321 | command: >
322 | validator
323 | --accept-terms-of-use
324 | --beacon-rpc-provider prysm-beacon-node-besu-el:4000
325 | --rpc
326 | --grpc-gateway-host 0.0.0.0
327 | --grpc-gateway-port 7500
328 | --force-clear-db
329 | --chain-config-file=/config_data/custom_config_data/config.yaml
330 | --suggested-fee-recipient 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b
331 | --wallet-password-file=/prysm_data/wallet_pass.txt
332 | --wallet-dir=/prysm_data/wallet
333 | --enable-tracing
334 | --tracing-endpoint http://jaeger-tracing:14268/api/traces
335 | --tracing-process-name validator-node
336 | --verbosity trace
337 | ports:
338 | - '7500:7500'
339 | volumes:
340 | - 'config_data:/config_data'
341 | - 'prysm_data:/prysm_data'
342 |
343 | lodestar-beacon-node:
344 | depends_on:
345 | bootnode:
346 | condition: service_started
347 | geth-3:
348 | condition: service_healthy
349 | genesis-generator:
350 | condition: service_completed_successfully
351 | image: g11tech/lodestar:4844-jan23
352 | environment:
353 | EXECUTION_NODE_URL: http://geth-3:8551
354 | entrypoint: ['run_beacon_node.sh']
355 | ports:
356 | - '3600:3500'
357 | - '14000:13000'
358 | volumes:
359 | - 'config_data:/config_data'
360 | - type: bind
361 | source: ./lodestar/run_beacon_node.sh
362 | target: /usr/local/bin/run_beacon_node.sh
363 |
364 | lodestar-validator-node:
365 | depends_on:
366 | - lodestar-beacon-node
367 | image: g11tech/lodestar:4844-jan23
368 | command: >
369 | validator
370 | --paramsFile /config_data/custom_config_data/config.yaml
371 | --force
372 | --dataDir /chaindata/validator
373 | --keystoresDir /lodestar_data/keystores
374 | --secretsDir /lodestar_data/secrets
375 | --server http://lodestar-beacon-node:3500
376 | --suggestedFeeRecipient 0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b
377 | volumes:
378 | - 'config_data:/config_data'
379 | - 'lodestar_data:/lodestar_data'
380 |
381 | lighthouse-beacon-node:
382 | <<: *lighthouse-bn
383 | depends_on:
384 | bootnode:
385 | condition: service_started
386 | geth-1:
387 | condition: service_started
388 | genesis-generator:
389 | condition: service_completed_successfully
390 | entrypoint: ["run_beacon_node.sh", "-d", "debug", "/data/node_1", "http://geth-1:8551"]
391 | environment:
392 | - P2P_PORT=9000
393 | ports:
394 | - "5052:5052"
395 | - "8000:8000"
396 | - "9000:9000"
397 |
398 | lighthouse-beacon-node-follower:
399 | <<: *lighthouse-bn
400 | depends_on:
401 | bootnode:
402 | condition: service_started
403 | geth-2:
404 | condition: service_started
405 | genesis-generator:
406 | condition: service_completed_successfully
407 | entrypoint: ["run_beacon_node.sh", "-d", "debug", "/data/node_2", "http://geth-2:8551"]
408 | environment:
409 | - P2P_PORT=9001
410 | ports:
411 | - "5053:5052"
412 | - "8001:8000"
413 | - "9001:9001"
414 |
415 | lighthouse-validator-node:
416 | image: inphi/lighthouse:eip4844
417 | depends_on:
418 | lighthouse-beacon-node:
419 | condition: service_started
420 | genesis-generator:
421 | condition: service_completed_successfully
422 | build:
423 | context: ./lighthouse
424 | dockerfile: Dockerfile.lighthouse
425 | command: >
426 | lighthouse
427 | --debug-level info
428 | vc
429 | --validators-dir /data/validators
430 | --secrets-dir /data/secrets
431 | --testnet-dir /config_data/custom_config_data
432 | --init-slashing-protection
433 | --beacon-nodes http://lighthouse-beacon-node:8000
434 | --suggested-fee-recipient 0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990
435 | volumes:
436 | - lighthouse_data:/data
437 | - config_data:/config_data
438 |
439 | jaeger-tracing:
440 | image: jaegertracing/all-in-one:1.35
441 | environment:
442 | COLLECTOR_ZIPKIN_HTTP_PORT: 9411
443 | ports:
444 | - '5775:5775/udp'
445 | - '6831:6831/udp'
446 | - '6832:6832/udp'
447 | - '5778:5778'
448 | - '16686:16686'
449 | - '14268:14268'
450 | - '9411:9411'
451 |
--------------------------------------------------------------------------------
/geth/geth-genesis.json:
--------------------------------------------------------------------------------
1 | {
2 | "config": {
3 | "chainId": 1,
4 | "homesteadBlock": 0,
5 | "eip150Block": 0,
6 | "eip155Block": 0,
7 | "eip158Block": 0,
8 | "byzantiumBlock": 0,
9 | "constantinopleBlock": 0,
10 | "petersburgBlock": 0,
11 | "istanbulBlock": 0,
12 | "berlinBlock": 0,
13 | "londonBlock": 0,
14 | "mergeNetsplitBlock": 0,
15 | "shanghaiTime": 0,
16 | "shardingForkTime": 0,
17 | "terminalTotalDifficulty": 0
18 | },
19 | "alloc": {
20 | "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
21 | "balance": "0x6d6172697573766477000000"
22 | },
23 | "0x0000000000000000000000000000000000000000": {
24 | "balance": "1"
25 | },
26 | "0x0000000000000000000000000000000000000001": {
27 | "balance": "1"
28 | },
29 | "0x0000000000000000000000000000000000000002": {
30 | "balance": "1"
31 | },
32 | "0x0000000000000000000000000000000000000003": {
33 | "balance": "1"
34 | },
35 | "0x0000000000000000000000000000000000000004": {
36 | "balance": "1"
37 | },
38 | "0x0000000000000000000000000000000000000005": {
39 | "balance": "1"
40 | },
41 | "0x0000000000000000000000000000000000000006": {
42 | "balance": "1"
43 | },
44 | "0x0000000000000000000000000000000000000007": {
45 | "balance": "1"
46 | },
47 | "0x0000000000000000000000000000000000000008": {
48 | "balance": "1"
49 | },
50 | "0x0000000000000000000000000000000000000009": {
51 | "balance": "1"
52 | },
53 | "0x000000000000000000000000000000000000000a": {
54 | "balance": "1"
55 | },
56 | "0x000000000000000000000000000000000000000b": {
57 | "balance": "1"
58 | },
59 | "0x000000000000000000000000000000000000000c": {
60 | "balance": "1"
61 | },
62 | "0x000000000000000000000000000000000000000d": {
63 | "balance": "1"
64 | },
65 | "0x000000000000000000000000000000000000000e": {
66 | "balance": "1"
67 | },
68 | "0x000000000000000000000000000000000000000f": {
69 | "balance": "1"
70 | },
71 | "0x0000000000000000000000000000000000000010": {
72 | "balance": "1"
73 | },
74 | "0x0000000000000000000000000000000000000011": {
75 | "balance": "1"
76 | },
77 | "0x0000000000000000000000000000000000000012": {
78 | "balance": "1"
79 | },
80 | "0x0000000000000000000000000000000000000013": {
81 | "balance": "1"
82 | },
83 | "0x0000000000000000000000000000000000000014": {
84 | "balance": "1"
85 | },
86 | "0x0000000000000000000000000000000000000015": {
87 | "balance": "1"
88 | },
89 | "0x0000000000000000000000000000000000000016": {
90 | "balance": "1"
91 | },
92 | "0x0000000000000000000000000000000000000017": {
93 | "balance": "1"
94 | },
95 | "0x0000000000000000000000000000000000000018": {
96 | "balance": "1"
97 | },
98 | "0x0000000000000000000000000000000000000019": {
99 | "balance": "1"
100 | },
101 | "0x000000000000000000000000000000000000001a": {
102 | "balance": "1"
103 | },
104 | "0x000000000000000000000000000000000000001b": {
105 | "balance": "1"
106 | },
107 | "0x000000000000000000000000000000000000001c": {
108 | "balance": "1"
109 | },
110 | "0x000000000000000000000000000000000000001d": {
111 | "balance": "1"
112 | },
113 | "0x000000000000000000000000000000000000001e": {
114 | "balance": "1"
115 | },
116 | "0x000000000000000000000000000000000000001f": {
117 | "balance": "1"
118 | },
119 | "0x0000000000000000000000000000000000000020": {
120 | "balance": "1"
121 | },
122 | "0x0000000000000000000000000000000000000021": {
123 | "balance": "1"
124 | },
125 | "0x0000000000000000000000000000000000000022": {
126 | "balance": "1"
127 | },
128 | "0x0000000000000000000000000000000000000023": {
129 | "balance": "1"
130 | },
131 | "0x0000000000000000000000000000000000000024": {
132 | "balance": "1"
133 | },
134 | "0x0000000000000000000000000000000000000025": {
135 | "balance": "1"
136 | },
137 | "0x0000000000000000000000000000000000000026": {
138 | "balance": "1"
139 | },
140 | "0x0000000000000000000000000000000000000027": {
141 | "balance": "1"
142 | },
143 | "0x0000000000000000000000000000000000000028": {
144 | "balance": "1"
145 | },
146 | "0x0000000000000000000000000000000000000029": {
147 | "balance": "1"
148 | },
149 | "0x000000000000000000000000000000000000002a": {
150 | "balance": "1"
151 | },
152 | "0x000000000000000000000000000000000000002b": {
153 | "balance": "1"
154 | },
155 | "0x000000000000000000000000000000000000002c": {
156 | "balance": "1"
157 | },
158 | "0x000000000000000000000000000000000000002d": {
159 | "balance": "1"
160 | },
161 | "0x000000000000000000000000000000000000002e": {
162 | "balance": "1"
163 | },
164 | "0x000000000000000000000000000000000000002f": {
165 | "balance": "1"
166 | },
167 | "0x0000000000000000000000000000000000000030": {
168 | "balance": "1"
169 | },
170 | "0x0000000000000000000000000000000000000031": {
171 | "balance": "1"
172 | },
173 | "0x0000000000000000000000000000000000000032": {
174 | "balance": "1"
175 | },
176 | "0x0000000000000000000000000000000000000033": {
177 | "balance": "1"
178 | },
179 | "0x0000000000000000000000000000000000000034": {
180 | "balance": "1"
181 | },
182 | "0x0000000000000000000000000000000000000035": {
183 | "balance": "1"
184 | },
185 | "0x0000000000000000000000000000000000000036": {
186 | "balance": "1"
187 | },
188 | "0x0000000000000000000000000000000000000037": {
189 | "balance": "1"
190 | },
191 | "0x0000000000000000000000000000000000000038": {
192 | "balance": "1"
193 | },
194 | "0x0000000000000000000000000000000000000039": {
195 | "balance": "1"
196 | },
197 | "0x000000000000000000000000000000000000003a": {
198 | "balance": "1"
199 | },
200 | "0x000000000000000000000000000000000000003b": {
201 | "balance": "1"
202 | },
203 | "0x000000000000000000000000000000000000003c": {
204 | "balance": "1"
205 | },
206 | "0x000000000000000000000000000000000000003d": {
207 | "balance": "1"
208 | },
209 | "0x000000000000000000000000000000000000003e": {
210 | "balance": "1"
211 | },
212 | "0x000000000000000000000000000000000000003f": {
213 | "balance": "1"
214 | },
215 | "0x0000000000000000000000000000000000000040": {
216 | "balance": "1"
217 | },
218 | "0x0000000000000000000000000000000000000041": {
219 | "balance": "1"
220 | },
221 | "0x0000000000000000000000000000000000000042": {
222 | "balance": "1"
223 | },
224 | "0x0000000000000000000000000000000000000043": {
225 | "balance": "1"
226 | },
227 | "0x0000000000000000000000000000000000000044": {
228 | "balance": "1"
229 | },
230 | "0x0000000000000000000000000000000000000045": {
231 | "balance": "1"
232 | },
233 | "0x0000000000000000000000000000000000000046": {
234 | "balance": "1"
235 | },
236 | "0x0000000000000000000000000000000000000047": {
237 | "balance": "1"
238 | },
239 | "0x0000000000000000000000000000000000000048": {
240 | "balance": "1"
241 | },
242 | "0x0000000000000000000000000000000000000049": {
243 | "balance": "1"
244 | },
245 | "0x000000000000000000000000000000000000004a": {
246 | "balance": "1"
247 | },
248 | "0x000000000000000000000000000000000000004b": {
249 | "balance": "1"
250 | },
251 | "0x000000000000000000000000000000000000004c": {
252 | "balance": "1"
253 | },
254 | "0x000000000000000000000000000000000000004d": {
255 | "balance": "1"
256 | },
257 | "0x000000000000000000000000000000000000004e": {
258 | "balance": "1"
259 | },
260 | "0x000000000000000000000000000000000000004f": {
261 | "balance": "1"
262 | },
263 | "0x0000000000000000000000000000000000000050": {
264 | "balance": "1"
265 | },
266 | "0x0000000000000000000000000000000000000051": {
267 | "balance": "1"
268 | },
269 | "0x0000000000000000000000000000000000000052": {
270 | "balance": "1"
271 | },
272 | "0x0000000000000000000000000000000000000053": {
273 | "balance": "1"
274 | },
275 | "0x0000000000000000000000000000000000000054": {
276 | "balance": "1"
277 | },
278 | "0x0000000000000000000000000000000000000055": {
279 | "balance": "1"
280 | },
281 | "0x0000000000000000000000000000000000000056": {
282 | "balance": "1"
283 | },
284 | "0x0000000000000000000000000000000000000057": {
285 | "balance": "1"
286 | },
287 | "0x0000000000000000000000000000000000000058": {
288 | "balance": "1"
289 | },
290 | "0x0000000000000000000000000000000000000059": {
291 | "balance": "1"
292 | },
293 | "0x000000000000000000000000000000000000005a": {
294 | "balance": "1"
295 | },
296 | "0x000000000000000000000000000000000000005b": {
297 | "balance": "1"
298 | },
299 | "0x000000000000000000000000000000000000005c": {
300 | "balance": "1"
301 | },
302 | "0x000000000000000000000000000000000000005d": {
303 | "balance": "1"
304 | },
305 | "0x000000000000000000000000000000000000005e": {
306 | "balance": "1"
307 | },
308 | "0x000000000000000000000000000000000000005f": {
309 | "balance": "1"
310 | },
311 | "0x0000000000000000000000000000000000000060": {
312 | "balance": "1"
313 | },
314 | "0x0000000000000000000000000000000000000061": {
315 | "balance": "1"
316 | },
317 | "0x0000000000000000000000000000000000000062": {
318 | "balance": "1"
319 | },
320 | "0x0000000000000000000000000000000000000063": {
321 | "balance": "1"
322 | },
323 | "0x0000000000000000000000000000000000000064": {
324 | "balance": "1"
325 | },
326 | "0x0000000000000000000000000000000000000065": {
327 | "balance": "1"
328 | },
329 | "0x0000000000000000000000000000000000000066": {
330 | "balance": "1"
331 | },
332 | "0x0000000000000000000000000000000000000067": {
333 | "balance": "1"
334 | },
335 | "0x0000000000000000000000000000000000000068": {
336 | "balance": "1"
337 | },
338 | "0x0000000000000000000000000000000000000069": {
339 | "balance": "1"
340 | },
341 | "0x000000000000000000000000000000000000006a": {
342 | "balance": "1"
343 | },
344 | "0x000000000000000000000000000000000000006b": {
345 | "balance": "1"
346 | },
347 | "0x000000000000000000000000000000000000006c": {
348 | "balance": "1"
349 | },
350 | "0x000000000000000000000000000000000000006d": {
351 | "balance": "1"
352 | },
353 | "0x000000000000000000000000000000000000006e": {
354 | "balance": "1"
355 | },
356 | "0x000000000000000000000000000000000000006f": {
357 | "balance": "1"
358 | },
359 | "0x0000000000000000000000000000000000000070": {
360 | "balance": "1"
361 | },
362 | "0x0000000000000000000000000000000000000071": {
363 | "balance": "1"
364 | },
365 | "0x0000000000000000000000000000000000000072": {
366 | "balance": "1"
367 | },
368 | "0x0000000000000000000000000000000000000073": {
369 | "balance": "1"
370 | },
371 | "0x0000000000000000000000000000000000000074": {
372 | "balance": "1"
373 | },
374 | "0x0000000000000000000000000000000000000075": {
375 | "balance": "1"
376 | },
377 | "0x0000000000000000000000000000000000000076": {
378 | "balance": "1"
379 | },
380 | "0x0000000000000000000000000000000000000077": {
381 | "balance": "1"
382 | },
383 | "0x0000000000000000000000000000000000000078": {
384 | "balance": "1"
385 | },
386 | "0x0000000000000000000000000000000000000079": {
387 | "balance": "1"
388 | },
389 | "0x000000000000000000000000000000000000007a": {
390 | "balance": "1"
391 | },
392 | "0x000000000000000000000000000000000000007b": {
393 | "balance": "1"
394 | },
395 | "0x000000000000000000000000000000000000007c": {
396 | "balance": "1"
397 | },
398 | "0x000000000000000000000000000000000000007d": {
399 | "balance": "1"
400 | },
401 | "0x000000000000000000000000000000000000007e": {
402 | "balance": "1"
403 | },
404 | "0x000000000000000000000000000000000000007f": {
405 | "balance": "1"
406 | },
407 | "0x0000000000000000000000000000000000000080": {
408 | "balance": "1"
409 | },
410 | "0x0000000000000000000000000000000000000081": {
411 | "balance": "1"
412 | },
413 | "0x0000000000000000000000000000000000000082": {
414 | "balance": "1"
415 | },
416 | "0x0000000000000000000000000000000000000083": {
417 | "balance": "1"
418 | },
419 | "0x0000000000000000000000000000000000000084": {
420 | "balance": "1"
421 | },
422 | "0x0000000000000000000000000000000000000085": {
423 | "balance": "1"
424 | },
425 | "0x0000000000000000000000000000000000000086": {
426 | "balance": "1"
427 | },
428 | "0x0000000000000000000000000000000000000087": {
429 | "balance": "1"
430 | },
431 | "0x0000000000000000000000000000000000000088": {
432 | "balance": "1"
433 | },
434 | "0x0000000000000000000000000000000000000089": {
435 | "balance": "1"
436 | },
437 | "0x000000000000000000000000000000000000008a": {
438 | "balance": "1"
439 | },
440 | "0x000000000000000000000000000000000000008b": {
441 | "balance": "1"
442 | },
443 | "0x000000000000000000000000000000000000008c": {
444 | "balance": "1"
445 | },
446 | "0x000000000000000000000000000000000000008d": {
447 | "balance": "1"
448 | },
449 | "0x000000000000000000000000000000000000008e": {
450 | "balance": "1"
451 | },
452 | "0x000000000000000000000000000000000000008f": {
453 | "balance": "1"
454 | },
455 | "0x0000000000000000000000000000000000000090": {
456 | "balance": "1"
457 | },
458 | "0x0000000000000000000000000000000000000091": {
459 | "balance": "1"
460 | },
461 | "0x0000000000000000000000000000000000000092": {
462 | "balance": "1"
463 | },
464 | "0x0000000000000000000000000000000000000093": {
465 | "balance": "1"
466 | },
467 | "0x0000000000000000000000000000000000000094": {
468 | "balance": "1"
469 | },
470 | "0x0000000000000000000000000000000000000095": {
471 | "balance": "1"
472 | },
473 | "0x0000000000000000000000000000000000000096": {
474 | "balance": "1"
475 | },
476 | "0x0000000000000000000000000000000000000097": {
477 | "balance": "1"
478 | },
479 | "0x0000000000000000000000000000000000000098": {
480 | "balance": "1"
481 | },
482 | "0x0000000000000000000000000000000000000099": {
483 | "balance": "1"
484 | },
485 | "0x000000000000000000000000000000000000009a": {
486 | "balance": "1"
487 | },
488 | "0x000000000000000000000000000000000000009b": {
489 | "balance": "1"
490 | },
491 | "0x000000000000000000000000000000000000009c": {
492 | "balance": "1"
493 | },
494 | "0x000000000000000000000000000000000000009d": {
495 | "balance": "1"
496 | },
497 | "0x000000000000000000000000000000000000009e": {
498 | "balance": "1"
499 | },
500 | "0x000000000000000000000000000000000000009f": {
501 | "balance": "1"
502 | },
503 | "0x00000000000000000000000000000000000000a0": {
504 | "balance": "1"
505 | },
506 | "0x00000000000000000000000000000000000000a1": {
507 | "balance": "1"
508 | },
509 | "0x00000000000000000000000000000000000000a2": {
510 | "balance": "1"
511 | },
512 | "0x00000000000000000000000000000000000000a3": {
513 | "balance": "1"
514 | },
515 | "0x00000000000000000000000000000000000000a4": {
516 | "balance": "1"
517 | },
518 | "0x00000000000000000000000000000000000000a5": {
519 | "balance": "1"
520 | },
521 | "0x00000000000000000000000000000000000000a6": {
522 | "balance": "1"
523 | },
524 | "0x00000000000000000000000000000000000000a7": {
525 | "balance": "1"
526 | },
527 | "0x00000000000000000000000000000000000000a8": {
528 | "balance": "1"
529 | },
530 | "0x00000000000000000000000000000000000000a9": {
531 | "balance": "1"
532 | },
533 | "0x00000000000000000000000000000000000000aa": {
534 | "balance": "1"
535 | },
536 | "0x00000000000000000000000000000000000000ab": {
537 | "balance": "1"
538 | },
539 | "0x00000000000000000000000000000000000000ac": {
540 | "balance": "1"
541 | },
542 | "0x00000000000000000000000000000000000000ad": {
543 | "balance": "1"
544 | },
545 | "0x00000000000000000000000000000000000000ae": {
546 | "balance": "1"
547 | },
548 | "0x00000000000000000000000000000000000000af": {
549 | "balance": "1"
550 | },
551 | "0x00000000000000000000000000000000000000b0": {
552 | "balance": "1"
553 | },
554 | "0x00000000000000000000000000000000000000b1": {
555 | "balance": "1"
556 | },
557 | "0x00000000000000000000000000000000000000b2": {
558 | "balance": "1"
559 | },
560 | "0x00000000000000000000000000000000000000b3": {
561 | "balance": "1"
562 | },
563 | "0x00000000000000000000000000000000000000b4": {
564 | "balance": "1"
565 | },
566 | "0x00000000000000000000000000000000000000b5": {
567 | "balance": "1"
568 | },
569 | "0x00000000000000000000000000000000000000b6": {
570 | "balance": "1"
571 | },
572 | "0x00000000000000000000000000000000000000b7": {
573 | "balance": "1"
574 | },
575 | "0x00000000000000000000000000000000000000b8": {
576 | "balance": "1"
577 | },
578 | "0x00000000000000000000000000000000000000b9": {
579 | "balance": "1"
580 | },
581 | "0x00000000000000000000000000000000000000ba": {
582 | "balance": "1"
583 | },
584 | "0x00000000000000000000000000000000000000bb": {
585 | "balance": "1"
586 | },
587 | "0x00000000000000000000000000000000000000bc": {
588 | "balance": "1"
589 | },
590 | "0x00000000000000000000000000000000000000bd": {
591 | "balance": "1"
592 | },
593 | "0x00000000000000000000000000000000000000be": {
594 | "balance": "1"
595 | },
596 | "0x00000000000000000000000000000000000000bf": {
597 | "balance": "1"
598 | },
599 | "0x00000000000000000000000000000000000000c0": {
600 | "balance": "1"
601 | },
602 | "0x00000000000000000000000000000000000000c1": {
603 | "balance": "1"
604 | },
605 | "0x00000000000000000000000000000000000000c2": {
606 | "balance": "1"
607 | },
608 | "0x00000000000000000000000000000000000000c3": {
609 | "balance": "1"
610 | },
611 | "0x00000000000000000000000000000000000000c4": {
612 | "balance": "1"
613 | },
614 | "0x00000000000000000000000000000000000000c5": {
615 | "balance": "1"
616 | },
617 | "0x00000000000000000000000000000000000000c6": {
618 | "balance": "1"
619 | },
620 | "0x00000000000000000000000000000000000000c7": {
621 | "balance": "1"
622 | },
623 | "0x00000000000000000000000000000000000000c8": {
624 | "balance": "1"
625 | },
626 | "0x00000000000000000000000000000000000000c9": {
627 | "balance": "1"
628 | },
629 | "0x00000000000000000000000000000000000000ca": {
630 | "balance": "1"
631 | },
632 | "0x00000000000000000000000000000000000000cb": {
633 | "balance": "1"
634 | },
635 | "0x00000000000000000000000000000000000000cc": {
636 | "balance": "1"
637 | },
638 | "0x00000000000000000000000000000000000000cd": {
639 | "balance": "1"
640 | },
641 | "0x00000000000000000000000000000000000000ce": {
642 | "balance": "1"
643 | },
644 | "0x00000000000000000000000000000000000000cf": {
645 | "balance": "1"
646 | },
647 | "0x00000000000000000000000000000000000000d0": {
648 | "balance": "1"
649 | },
650 | "0x00000000000000000000000000000000000000d1": {
651 | "balance": "1"
652 | },
653 | "0x00000000000000000000000000000000000000d2": {
654 | "balance": "1"
655 | },
656 | "0x00000000000000000000000000000000000000d3": {
657 | "balance": "1"
658 | },
659 | "0x00000000000000000000000000000000000000d4": {
660 | "balance": "1"
661 | },
662 | "0x00000000000000000000000000000000000000d5": {
663 | "balance": "1"
664 | },
665 | "0x00000000000000000000000000000000000000d6": {
666 | "balance": "1"
667 | },
668 | "0x00000000000000000000000000000000000000d7": {
669 | "balance": "1"
670 | },
671 | "0x00000000000000000000000000000000000000d8": {
672 | "balance": "1"
673 | },
674 | "0x00000000000000000000000000000000000000d9": {
675 | "balance": "1"
676 | },
677 | "0x00000000000000000000000000000000000000da": {
678 | "balance": "1"
679 | },
680 | "0x00000000000000000000000000000000000000db": {
681 | "balance": "1"
682 | },
683 | "0x00000000000000000000000000000000000000dc": {
684 | "balance": "1"
685 | },
686 | "0x00000000000000000000000000000000000000dd": {
687 | "balance": "1"
688 | },
689 | "0x00000000000000000000000000000000000000de": {
690 | "balance": "1"
691 | },
692 | "0x00000000000000000000000000000000000000df": {
693 | "balance": "1"
694 | },
695 | "0x00000000000000000000000000000000000000e0": {
696 | "balance": "1"
697 | },
698 | "0x00000000000000000000000000000000000000e1": {
699 | "balance": "1"
700 | },
701 | "0x00000000000000000000000000000000000000e2": {
702 | "balance": "1"
703 | },
704 | "0x00000000000000000000000000000000000000e3": {
705 | "balance": "1"
706 | },
707 | "0x00000000000000000000000000000000000000e4": {
708 | "balance": "1"
709 | },
710 | "0x00000000000000000000000000000000000000e5": {
711 | "balance": "1"
712 | },
713 | "0x00000000000000000000000000000000000000e6": {
714 | "balance": "1"
715 | },
716 | "0x00000000000000000000000000000000000000e7": {
717 | "balance": "1"
718 | },
719 | "0x00000000000000000000000000000000000000e8": {
720 | "balance": "1"
721 | },
722 | "0x00000000000000000000000000000000000000e9": {
723 | "balance": "1"
724 | },
725 | "0x00000000000000000000000000000000000000ea": {
726 | "balance": "1"
727 | },
728 | "0x00000000000000000000000000000000000000eb": {
729 | "balance": "1"
730 | },
731 | "0x00000000000000000000000000000000000000ec": {
732 | "balance": "1"
733 | },
734 | "0x00000000000000000000000000000000000000ed": {
735 | "balance": "1"
736 | },
737 | "0x00000000000000000000000000000000000000ee": {
738 | "balance": "1"
739 | },
740 | "0x00000000000000000000000000000000000000ef": {
741 | "balance": "1"
742 | },
743 | "0x00000000000000000000000000000000000000f0": {
744 | "balance": "1"
745 | },
746 | "0x00000000000000000000000000000000000000f1": {
747 | "balance": "1"
748 | },
749 | "0x00000000000000000000000000000000000000f2": {
750 | "balance": "1"
751 | },
752 | "0x00000000000000000000000000000000000000f3": {
753 | "balance": "1"
754 | },
755 | "0x00000000000000000000000000000000000000f4": {
756 | "balance": "1"
757 | },
758 | "0x00000000000000000000000000000000000000f5": {
759 | "balance": "1"
760 | },
761 | "0x00000000000000000000000000000000000000f6": {
762 | "balance": "1"
763 | },
764 | "0x00000000000000000000000000000000000000f7": {
765 | "balance": "1"
766 | },
767 | "0x00000000000000000000000000000000000000f8": {
768 | "balance": "1"
769 | },
770 | "0x00000000000000000000000000000000000000f9": {
771 | "balance": "1"
772 | },
773 | "0x00000000000000000000000000000000000000fa": {
774 | "balance": "1"
775 | },
776 | "0x00000000000000000000000000000000000000fb": {
777 | "balance": "1"
778 | },
779 | "0x00000000000000000000000000000000000000fc": {
780 | "balance": "1"
781 | },
782 | "0x00000000000000000000000000000000000000fd": {
783 | "balance": "1"
784 | },
785 | "0x00000000000000000000000000000000000000fe": {
786 | "balance": "1"
787 | },
788 | "0x00000000000000000000000000000000000000ff": {
789 | "balance": "1"
790 | },
791 | "0x8A04d14125D0FDCDc742F4A05C051De07232EDa4": {
792 | "balance": "0",
793 | "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a26469706673582212201dd26f37a621703009abf16e77e69c93dc50c79db7f6cc37543e3e0e3decdc9764736f6c634300060b0033",
794 | "storage": {
795 | "0x0000000000000000000000000000000000000000000000000000000000000022": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b",
796 | "0x0000000000000000000000000000000000000000000000000000000000000023": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71",
797 | "0x0000000000000000000000000000000000000000000000000000000000000024": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c",
798 | "0x0000000000000000000000000000000000000000000000000000000000000025": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c",
799 | "0x0000000000000000000000000000000000000000000000000000000000000026": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30",
800 | "0x0000000000000000000000000000000000000000000000000000000000000027": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1",
801 | "0x0000000000000000000000000000000000000000000000000000000000000028": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c",
802 | "0x0000000000000000000000000000000000000000000000000000000000000029": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193",
803 | "0x000000000000000000000000000000000000000000000000000000000000002a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1",
804 | "0x000000000000000000000000000000000000000000000000000000000000002b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b",
805 | "0x000000000000000000000000000000000000000000000000000000000000002c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220",
806 | "0x000000000000000000000000000000000000000000000000000000000000002d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f",
807 | "0x000000000000000000000000000000000000000000000000000000000000002e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e",
808 | "0x000000000000000000000000000000000000000000000000000000000000002f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784",
809 | "0x0000000000000000000000000000000000000000000000000000000000000030": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb",
810 | "0x0000000000000000000000000000000000000000000000000000000000000031": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb",
811 | "0x0000000000000000000000000000000000000000000000000000000000000032": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab",
812 | "0x0000000000000000000000000000000000000000000000000000000000000033": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4",
813 | "0x0000000000000000000000000000000000000000000000000000000000000034": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f",
814 | "0x0000000000000000000000000000000000000000000000000000000000000035": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa",
815 | "0x0000000000000000000000000000000000000000000000000000000000000036": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c",
816 | "0x0000000000000000000000000000000000000000000000000000000000000037": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167",
817 | "0x0000000000000000000000000000000000000000000000000000000000000038": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7",
818 | "0x0000000000000000000000000000000000000000000000000000000000000039": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0",
819 | "0x000000000000000000000000000000000000000000000000000000000000003a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544",
820 | "0x000000000000000000000000000000000000000000000000000000000000003b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765",
821 | "0x000000000000000000000000000000000000000000000000000000000000003c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4",
822 | "0x000000000000000000000000000000000000000000000000000000000000003d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1",
823 | "0x000000000000000000000000000000000000000000000000000000000000003e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636",
824 | "0x000000000000000000000000000000000000000000000000000000000000003f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c",
825 | "0x0000000000000000000000000000000000000000000000000000000000000040": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7"
826 | }
827 | },
828 | "0x9a4aa7d9C2F6386e5F24d790eB2FFB9fd543A170": {
829 | "balance": "1000000000000000000000000000"
830 | },
831 | "0x5E3141B900ac5f5608b0d057D10d45a0e4927cD9": {
832 | "balance": "1000000000000000000000000000"
833 | },
834 | "0x7cF5Dbc49F0904065664b5B6C0d69CaB55F33988": {
835 | "balance": "1000000000000000000000000000"
836 | },
837 | "0x8D12b071A6F3823A535D38C4a583a2FA1859e822": {
838 | "balance": "1000000000000000000000000000"
839 | },
840 | "0x3B575D3cda6b30736A38B031E0d245E646A21135": {
841 | "balance": "1000000000000000000000000000"
842 | },
843 | "0x53bDe6CF93461674F590E532006b4022dA57A724": {
844 | "balance": "1000000000000000000000000000"
845 | }
846 | },
847 | "coinbase": "0x0000000000000000000000000000000000000000",
848 | "difficulty": "0x01",
849 | "extraData": "",
850 | "gasLimit": "0x400000",
851 | "nonce": "0x1234",
852 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
853 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
854 | "timestamp": "1662465600"
855 | }
856 |
--------------------------------------------------------------------------------