├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── besu ├── Dockerfile.besu ├── besu.sh └── log4j2.xml ├── blob_tx_generator ├── README.md ├── blob.js ├── package-lock.json └── package.json ├── docker-compose.yml ├── download └── main.go ├── erigon ├── Dockerfile.erigon └── erigon.sh ├── eth.png ├── geth ├── .dockerignore ├── Dockerfile.geth ├── bootnode.sh ├── geth-genesis.json └── geth.sh ├── go.mod ├── go.sum ├── lighthouse ├── Dockerfile.lighthouse ├── deploy_block.txt ├── generate-genesis.sh ├── genesis.json ├── run_beacon_node.sh ├── run_bootnode.sh └── setup.sh ├── lodestar ├── .dockerignore ├── Dockerfile.lodestar └── run_beacon_node.sh ├── nethermind └── nethermind.sh ├── point_evaluation_tx ├── PointEvaluationTest.sol ├── README.md ├── compile.js ├── package.json ├── pointEvaluationTest.js └── yarn.lock ├── prysm ├── .dockerignore ├── Dockerfile.prysm └── run_beacon_node.sh ├── shared ├── blobs.go ├── config.go ├── genesis-generator-configs │ ├── cl │ │ ├── config.yaml │ │ └── mnemonics.yaml │ ├── el │ │ └── genesis-config.yaml │ └── values.env ├── genesis.json ├── jwtsecret ├── run_genesis_generator.sh ├── trusted_setup.txt └── util.go ├── tests ├── blobtx │ └── main.go ├── ctrl │ ├── bootstrap.go │ ├── ctrl.go │ └── services.go ├── fee-market │ └── main.go ├── initial-sync │ └── main.go ├── pre-4844 │ └── main.go └── util │ ├── download.go │ └── util.go └── upload └── main.go /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | .vscode/ 3 | node_modules/ 4 | shared/generated-configs 5 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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"] -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /eth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inphi/eip4844-interop/8be3960ccac20ab48db2daf74bceab7ca23deb50/eth.png -------------------------------------------------------------------------------- /geth/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | **/*_tests.go 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lighthouse/deploy_block.txt: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lodestar/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | node_modules 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | }); -------------------------------------------------------------------------------- /prysm/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | **/bazel* 4 | **/*_tests.go 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /shared/jwtsecret: -------------------------------------------------------------------------------- 1 | 0xfad2709d0bb03bf0e8ba3c99bea194575d3e98863133d1af638ed056d1d59345 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------