→ updated on 09/28/2022 for long-term Goerli/Prater testnet (previously Ropsten)
Shoutout to realbigsean from SigmaPrime for his guidance and insights and Yorick from EthStaker for feedback and review
This post aims to provide a quick and minimalistic setup guide on how to test accessing MEV as a validator on the Goerli/Prater testnet.
In a preceding guide I tried to highlight essential requirements for running an Ethereum node post-merge using Ubuntu & Docker. With mev-boost
as an optional, additional piece of software in the node stack, I try to leverage docker-compose
this time.
Maximum Extractable Value is a controversial topic within the Ethereum ecosystem. As the network has experienced increased adoption and settlement value, profitable opportunities to insert TXs at particular places of a block have become more prevalent.
These practices are often referred to as front-running, among others, implying centralisation vectors and even chain instabilities. Hence, the ultimate goal for blockchain R&D is to minimize (or eliminate?!) such negative externalities for users and the network as a whole. A first step towards mitigating them is to democratize access to MEV.
In essence, MEV boils down to the fundamental question of how the process of fair transaction ordering in a block is supposed to happen in the most trust-minimised fashion.
Technically, a validator -representing the proposer of a block- defaults to asking the local execution client for a particular transaction order (typically ranked by the highest paying TX fees). However, there’s also the option to ask a third-party relay for a pre-constructed block, e.g. Flashbots, a collective operating an external block builder protocol. A relay is a doubly-trusted intermediary matching specialised builders on the one hand with validators on the other hand.
Node operators can access MEV with the help of a software called mev-boost
by Flashbots. In the given case, if it’s the validator’s turn to propose a block, the CL client Lighthouse will ask both mev-boost and the EL client Besu for a block, ultimately opting for the better paying one.
Note:
In case a relay provided the validator with an invalid pre-constructed block (‘execution payload header’), which turned out to be invalid after the validator client had signed it, this does not represent a slashable event (rather leads to a missed proposal).
Read about how Flashbots argues for running mev-boost. And feel free to dive deeper into the woods here, here and here. There’s a bunch of open social, technical and cryptoeconomic research and design questions to be tackled.
For the sake of this demo, I chose Besu by ConsenSys as an EL client. It comes with a lean new database structure called “Bonsai tries” and offers implicit pruning functionality, lowering disk space usage and avoiding potential downtimes for manual pruning. Snap sync is also about to hit the latest release. Lighthouse by SigmaPrime serves as CL client (i.e. Beacon Node & Validator Client).
Specify --suggested-fee-recipient
Optionally modify the data directories that get mounted from the host into the Besu/Lighthouse docker containers
1) Navigate to your /home
directory. Create a project directory with subfolders:
$ mkdir -p ETHclient/{besu,lighthouse-bn,lighthouse-vc,validator_keys,JWT}; cd ETHclient
2) Generate a JasonWebToken (JWT) so that the execution and consensus client can communicate securely:
$ openssl rand -hex 32 | tr -d "\n" > "$(pwd)/JWT/jwtsecret"
3) Paste the validator keystore(s) in the host directory ./validator_keys
4) Add the validator key to Lighthouse
$ docker run -it -v $(pwd)/lighthouse-vc:/root/.lighthouse -v $(pwd)/validator_keys:/validator_keys sigp/lighthouse:latest-modern lighthouse --network prater account validator import --directory /validator_keys
5) Copy & Paste the below attached docker-compose code in a newly created file to be named besu-lh-mevboost.yaml
in the project directory.
6) Start the docker-compose environment:
$ docker-compose -f besu-lh-mevboost.yaml -p prater up -d
7) Eventually, stop the docker-compose environment:
$ docker-compose -f besu-lh-mevboost.yaml -p prater stop
version: "3.0"
services:
beacon_node:
image: sigp/lighthouse:latest-modern
networks:
- ETHclient_net
container_name: prater-bn
restart: unless-stopped
volumes:
- ./lighthouse-bn:/root/.lighthouse
- ./JWT:/JWT
ports:
- 9000:9000/tcp # consensus p2p, open to internet
- 9000:9000/udp # consensus p2p, open to internet
command: >
lighthouse
--debug-level info
--network prater
beacon_node
--datadir /root/.lighthouse/prater
--http
--http-address 0.0.0.0
--execution-endpoint http://goerli-besu:8551
--execution-jwt /JWT/jwtsecret
--checkpoint-sync-url https://goerli.beaconstate.info,https://goerli.checkpoint-sync.ethdevops.io,https://goerli.beaconstate.ethstaker.cc/
--builder http://prater-mevboost:18550
validator_client:
image: sigp/lighthouse:latest-modern
networks:
- ETHclient_net
container_name: prater-vc
restart: unless-stopped
volumes:
- ./lighthouse-vc:/root/.lighthouse
depends_on:
- beacon_node
command: >
lighthouse
--debug-level info
--network prater
validator
--beacon-nodes http://prater-bn:5052
--suggested-fee-recipient 0x0000000000000000000000000000000000000000
--builder-proposals
execution_client:
image: hyperledger/besu:latest
networks:
- ETHclient_net
container_name: goerli-besu
restart: on-failure
environment:
- "JAVA_OPTS=-Xmx4g"
volumes:
- ./besu:/opt/besu/data
- ./JWT:/JWT
ports:
- 30303:30303/tcp # consensus p2p, open to internet
- 30303:30303/udp # consensus p2p, open to internet
command: >
--network goerli
--data-path /opt/besu/data
--data-storage-format BONSAI
--sync-mode X_SNAP
--rpc-http-enabled
--rpc-http-host=0.0.0.0
--rpc-http-api=ETH,NET,WEB3
--rpc-http-cors-origins=*
--host-allowlist=*
--engine-host-allowlist=*
--engine-jwt-secret=/JWT/jwtsecret
--engine-rpc-port=8551
mev_boost:
image: flashbots/mev-boost:latest
networks:
- ETHclient_net
container_name: prater-mevboost
restart: on-failure
entrypoint:
- /app/mev-boost
- -addr
- 0.0.0.0:18550
- -prater
- -relay-check
- -relays
- https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@builder-relay-goerli.flashbots.net,https://0x821f2a65afb70e7f2e820a925a9b4c80a159620582c1766b1b09729fec178b11ea22abb3a51f07b288be815a1a2ff516@bloxroute.max-profit.builder.goerli.blxrbdn.com
networks:
ETHclient_net:
driver: bridge
Obviously, the more testnet validators you run, the higher the likelihood of being randomly selected to propose a block (i.e. you likely have to wait less long)
Watch out for the following logs in mev-boost:
level=info msg="getHeader: successfully got more valuable payload header"
level=info msg="getPayload: received payload from relay"
And check if your block has made it on-chain:
https://prater.etherscan.io/block/<yourblocknumber>
****
Please keep in mind this is a testnet guide that may contain mistakes and that takes shortcuts which come with trade-offs. It could quickly become outdated as it’s subject to ever evolving network and client changes.
****
This post was supported by a grant from a CLR funding round held by EthStaker, mainly matched by the EF.