diff --git a/README.md b/README.md index 8310a5c..ec3864d 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Status | Network | Links | Ansible 🔴 | [devnet-4](https://notes.ethereum.org/@ethpandaops/glamsterdam-devnet-4)| [Network config](network-configs/devnet-4) | - | - | [🔗](kubernetes/devnet-4) Off | [devnet-5](https://notes.ethereum.org/@ethpandaops/glamsterdam-devnet-5)| [Network config](network-configs/devnet-5) | - | - | [🔗](kubernetes/devnet-5) On | [devnet-6](https://notes.ethereum.org/@ethpandaops/glamsterdam-devnet-6)| [Network config](network-configs/devnet-6) | - | - | [🔗](kubernetes/devnet-6) +WIP | [devnet-7](https://notes.ethereum.org/@ethpandaops/glamsterdam-devnet-7)| [Network config](network-configs/devnet-7) | - | - | - # Development ## Version management for tools diff --git a/ansible/inventories/devnet-7/group_vars/all/00-defaults.yaml b/ansible/inventories/devnet-7/group_vars/all/00-defaults.yaml new file mode 120000 index 0000000..aa09835 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/all/00-defaults.yaml @@ -0,0 +1 @@ +../../../../group_vars/all/defaults.yaml \ No newline at end of file diff --git a/ansible/inventories/devnet-7/group_vars/all/all.sops.yaml b/ansible/inventories/devnet-7/group_vars/all/all.sops.yaml new file mode 100644 index 0000000..da3c771 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/all/all.sops.yaml @@ -0,0 +1,198 @@ +secret_zerossl: + ACME_EAB_KID: ENC[AES256_GCM,data:UAbtTHRnXHispR1iQr/5eQMV7JMk5g==,iv:WkDvniP4a1cjpRTQqT0MnJo9UkIp+iwJ3NPSTb8sFsU=,tag:yXavQCYBVJsPHwmm2WbwSg==,type:str] + ACME_EAB_HMAC_KEY: ENC[AES256_GCM,data:V7U8TluhY0y550IjMfubUgHSFCdxqS109TwWjSGx818J2So5zrTJR0TWzRl1bH4/9MyqCpEtTLHzLlL0j7I4XzgNvrm8qjpeNw3yRa94eyWH5EJT8VI=,iv:Evd5W8ORLtUa3sSl6XeaqY69p0FAmpiFmjXDlP+XbUY=,tag:A34c1/jnfKSFjuDf/2stYA==,type:str] +secret_prometheus_remote_write: + username: ENC[AES256_GCM,data:vbu1SEJ0RRPGBJMvB4P4dXwm4Fw=,iv:H71TQAsRE+ExK7Kp+xxYymDKMvNIM9MW47V/MpbHbY8=,tag:ORA9q6XgC4ivfmvDO2v1nQ==,type:str] + password: ENC[AES256_GCM,data:FsxhGsp3XUSWEZbJVYFv2V6L5HYEv6mNSfxdRWPZSkOvTG4B,iv:JkK+me7CBgP44gwHwZdCfpd5ZKhUmG9Pr7uPelW0JuM=,tag:LjbmI9MHSeyLkPl1Vg5Nyw==,type:str] +secret_loki: + endpoint: ENC[AES256_GCM,data:fgNKy2JFJ+eWraqSN8mmrMD2qua/od9Ai4KkWt0BaqJuJeNbkisVERcjZXL/OFZK0eJqNVzQh+w=,iv:3hdFH0Y+33EXpCMoEhQHMj1cwxn2EMFOdPj2YwJZAhg=,tag:H6KbRsCGIC0axq4qFfP++A==,type:str] + username: ENC[AES256_GCM,data:GMEZa00QcrMf5F0mvt3EgynIXcc=,iv:Lh39KtRrXz+nxvoX1sfCv5aQhY600tIxvFqOYRDqiJU=,tag:oZFvJo2In5VuKCL0F06lJA==,type:str] + password: ENC[AES256_GCM,data:Lup8xKhAXpbSjGU1aICYM4CR0jHzm0FYSz4a38H/psHLSHln,iv:576Quu09HRoruCzO9l1j6b9wlO0er/NKe1HTxNJudzc=,tag:+Vb16lFEFYR4JfpvOYZV1w==,type:str] +secret_nginx_shared_basic_auth: + name: ENC[AES256_GCM,data:O7L3,iv:DiOGlqfOfrDlt7X4OGY27OYlkDDEHguv+kg1zRhBek0=,tag:KuOX20lI/iYZOORFraiISw==,type:str] + password: ENC[AES256_GCM,data:HyMDeoK5s6JDfsJ7j5Gg,iv:yvC4WWOOJvVeegWl0uj2P8yxcfBgNrvYSm5xUB5qux8=,tag:XoZbXpvLtUJPVoEaV3ltsg==,type:str] +secret_ethstats: ENC[AES256_GCM,data:vO/gY2iluciwksE=,iv:WHYxXgQ2LdLGMKxvagmT3UhmQl/dRucpyhYzZxHvLHc=,tag:NX4Lkg6SPPusRx/zHGn00w==,type:str] +secret_bootnodoor_seed: ENC[AES256_GCM,data:A611+/dn9lT08iGrqgLNgqPpWGEyGf1usFFWM4P9uB0Twfryjk5bzBVFLcLPKzkhuj4DThToS8F3s4iCAmq/+Q==,iv:ic2cap4ynWi/IS6dkn3fkKgNTndnZCxTbBbIABpXgrA=,tag:1Jwqi77lNG3yXZ9RZnZe5g==,type:str] +secret_genesis_mnemonic: ENC[AES256_GCM,data:zRxUC65Kt1b8DM4QPyEJ/u0Uhp2ftMcJVtacbXRrOCUGm+gy0n+VH199hVxvviouJQkbv5FiJYiFjwjJ/yFX5ajPgOqanjRpBfEfwwD2Drtafaw/mWT7pSILHlo7oI18+bGIWjXve5mpUSuAC7jiaufDVROvSa6bcew0Ogbp5pzbFJBFFim3LFjyC1uSRO7JbRoK4cUl1CxnaCUaC/3GKz4l/fIe,iv:EaQilsQbnswzCqkWSK9G/R3wwmMRDwWZDjdZQ1fUZRk=,tag:2kWU1BNG/B82qzmNxmwIjQ==,type:str] +secret_mev_coinbase_secret_key: ENC[AES256_GCM,data:TYLALYjoxOyyrpPoJ/gBvXO2vMdbbFqrI0gyZAJ7MHgI7SMmb7qTfDzakHmOASZ5ezJXfOMWqy0zBqQwymLhzA==,iv:MNJfTjd3pfAW9tR8WUEcp5BOcjjBTQFRoAN+NkC+VAA=,tag:dlR3sMxJb9he8xZn3FcD6w==,type:str] +secret_mev_builder_tx_signing_key: ENC[AES256_GCM,data:Tr01nA3sls3AhttJga/ndK+nMjZSiyMIE3zafwsEZjZt9aETG+zEnkcAK5y9P1aq2N1UZ/KMOF0BPNbgCtOddw==,iv:tBmNG6Esy/3HFCiNZIggEb2Xlgc5MEwS4mVgQpcuSyc=,tag:2mfyvSzaMvCqeIFQGV7NMA==,type:str] +secret_mev_optimistic_relay_secret_key: ENC[AES256_GCM,data:S+hUZla9PQRe22mOoT4qy839Slvej86L2SduROkh3JlMDlnQDtAODv1nRI48JaXT2pBrr5cK7zYSaFICuU6+3w==,iv:9LupHVxZ+DMqY4ZAV8tGcjUuXSvjW8aroJ6HG8psauk=,tag:acL6x1VMrbh0XbXtrQk++g==,type:str] +secret_mev_relay_secret_key: ENC[AES256_GCM,data:jnk2Bp3kMu4XBAfe3cJ8iJ061Lq2UDoE5OLiGss1kmmh1W9x/7PVDRH/y6ysL2kS8vbIlTNPtsK8oRQk7MMXLQ==,iv:2QmzoczmbyFyB4UnmvoZDeAEXjIzXnFBY+acmVA6Ins=,tag:RwjvzIB8l2V5F912IAicvQ==,type:str] +secret_mev_flood_private_key: ENC[AES256_GCM,data:LYJDMo2JUMH5TnRJ/DiTpeF+u5oKTRJzpLwNte1QA84e8JNtKlehUDH5XX+rzoyOI3edHm4gwZ5oIeziY1STJA==,iv:1qNfUVqJ9eebRmA4Ly59KFR9WmaQ8eelSvNLJAWCJ10=,tag:2YTI3l+1z9sO2Gr7Csd4aw==,type:str] +secret_mev_flood_user_key: ENC[AES256_GCM,data:KKnBt1BEnBR/Cl9B7FljzV3kJOW9Gmp7DJ46xc1EkUZmWn1f8F4QLhgsyK34FKBKSCmDMCQoctwirAKTG/Vlsw==,iv:efkOBcrGeGJwT91SR+2wFI/fpnWASQeqjH3MJSIQBAA=,tag:1f+bkUNLh8kMsxO5qNQo+Q==,type:str] +secret_xatu_sentry: + server_address: ENC[AES256_GCM,data:m4TazcSCuGW/Jlh0Jg1G/0FyPpc6M4kHBt1SNXCSkkKMOPLenEBqHHl4pPlBuvfv+75E,iv:eCJatIrq7Ff8SJlMCSMxNBgV+iHFOlXcfer6/drqXCo=,tag:67qe1jg2i90xFq71s0rA/A==,type:str] + user: ENC[AES256_GCM,data:uwGLpiNh7Q==,iv:1beJ8Z7mcZzY55qRoYeoOHLluwAOVb4woG7vqF22x/w=,tag:aP85BC6u1T5dDETRRj569g==,type:str] + password: ENC[AES256_GCM,data:XCMEbRpVmRFaI10es9mBrR8EiFWJenK3zjM4QGh70iLo8HaU,iv:++cb9FjM21r9gylwvA5ax1E0FAQzZisAsJhGnPjQHdM=,tag:V5iTEAQ4JiUiiYeQqbMtDQ==,type:str] + event_ingester_auth: ENC[AES256_GCM,data:xD1uqYrRdB+TU7Fqbs+fENWiInVD1HOdVjxppQHhh7jRl+dU9HlJpD60nswd611EyHbWheTnlY1lRMO6Damz0ut9,iv:bKNxaPz+Xj5bIidQlaJrQXaDtT/I0lfBBY3zFfvMCAg=,tag:G1qOLehH6Wm9aI6a2ShqCQ==,type:str] + coordinator_secret: ENC[AES256_GCM,data:f4y7Ck0Z2zpOo8bHuS/w7Bhk6/6+DIvFXctMhjaMBYWLiwEY,iv:gQG7HBaDQPz0huJH2fq4y8HOGN3JwceJET/spgL5GXk=,tag:2guBo0MRKOMKOhpyjYL0VA==,type:str] +secret_cert_encryption_psk: ENC[AES256_GCM,data:WXK5OSHqbAPXxWRwxDV4vSkWLd+KBMJMZuEPFUKE,iv:w45No4rBDjT+kGooIoHGikjJQjwWQ9dyNBzL/NBo7XE=,tag:9Unr8yDbWpCOINcCW/frSg==,type:str] +secret_dora_api_key: ENC[AES256_GCM,data:RGBJ+xkv1WscCVO/Rd9/XZdOZatYCtSAKm/8yKVkwVr+fu0EB9LY7EG8fkw=,iv:YkgdX3hW+P53br6/uiNmmq4/qAyQ0/QrXSPgOjaMbVY=,tag:b/B9OBzYyWfJ5NemKpv2eQ==,type:str] +tx_fuzz_blobs_privkey: ENC[AES256_GCM,data:lYDYBAfPdtgovoGtmz7c4lUfiqxr2N0Sp1dkMWn115+TJYBzfpsMdfBl88PfAibFfHwhMhKwpvKTyHSeUzX2xw==,iv:qTk22/lpJyWFycayBupQp0sBaw2E2oq7peWypQh+0Ic=,tag:JhQPq8s3ueLEfL2FJGH+wA==,type:str] +tx_fuzz_txs_privkey: ENC[AES256_GCM,data:5oacWB7naaDLkIihcBK8E2AKPk9OWLz0AJ7ZLKmpTQkstF/idYkjm6GzmaX0LVGxND0DlUO/fcydtY+UFVQoyw==,iv:gHO+ttqzJCbRBCUvmlsTVNQK6vpnGXamIag63HS/fas=,tag:Ub7xqXrx9tYU2ytk/XtJCQ==,type:str] +goomy_private_key: ENC[AES256_GCM,data:cpKpCZPZOZ/oA4zSFJaIutD/H5SUOHLBS/qyKL+5ZOMOvaDVmI3zY3GiQl149LB3tya6Rg2rEQSABxR1fz8c5w==,iv:Ibm4sLvU+tNk7EqaSPs/2CRLU1yLlNDgSWjR+uNPrvQ=,tag:sOvyhcNsNv2Zj/7waeZBBA==,type:str] +nethermind_seq_api_key: ENC[AES256_GCM,data:C6dHJX02l+m0zVYH691GUyKZgRs=,iv:tNCA6W6hJm1PybTE2piAhfUjDKCqQWeN2FQfgiayJC0=,tag:VZiwt+DgR+nfWXdzZjGnxw==,type:str] +nethermind_seq_server: ENC[AES256_GCM,data:wEIM2pUqK+I3rf42maFTr05r8cOJWjnpPQ==,iv:2Iu5nQAjRmxqkOF2Pueup+Kiwd34sfTxWY8aFv0pea0=,tag:VrfL6pUITsKJPgSue/XXJw==,type:str] +nethermind_push_gateway: ENC[AES256_GCM,data:ScReyCHKHx2/8Yo3epSAsb3MykmlIrGkUegaV5+21aboPCgU+VbUOBfvqaTWpcQN3OjXH+euQj1bHL1etGk1qvjzm2sp/P9oqbHRU827nbejkk5SKEeC4q79U7QyQTbUJ1Z9paX1sYZysMDUf2LUb9r/wHhB7bknQYKnHBAg0Li7qw5T5o1yatJoDqfahe4R1Q==,iv:WuQDvBvS2zjEB805HYkq5TQ/H8baF23vyMP9JbdqCao=,tag:jTOnfh2+ZmWIXQBzIpIVCQ==,type:str] +tysm_secret_key: ENC[AES256_GCM,data:MuvclVLaNVZ+7vRumg==,iv:XGBLMISj2wL7MQznXnVggjudiaw6Ff4i3wGt37/EKqA=,tag:ib5t0B5qa1FD6Ff8xYIFwQ==,type:str] +tempo_grpc_url: ENC[AES256_GCM,data:ltAVTGgrqhUBXdAZe7D1HvdXK72YIORL/x4DYHgX911s+X8IZM9/guRqE1I/ZYSzNrQX0qON3/TrNSjpG1BUpkK9M0SLzqE4EKhaOOQonJRLunnufZVrZIDhXSMaGQhZcjsHQCV8,iv:4mzqA4Ck1g91+tST5oTSnTepikjOCWKJrV04Rsp/8Ts=,tag:MgjQUb+lR1SIU2d8KpvOew==,type:str] +secret_buildoor_wallet_privkey: ENC[AES256_GCM,data:MA5s/epX0pOuU/EsKlc3oXdo/jdjGazQB8aV50ANsIsiMqRbVb+/+BmDTvPAGldEkEO+NWziEE21asYJMi0uHQ==,iv:j/sex4nBa5pFOHXX6zlpJGGCxUcdxZPfHSnLA7kl0mo=,tag:BrMMLbCzwVPzFYNaClL57g==,type:str] +secret_buildoor_builder_mnemonic: ENC[AES256_GCM,data:UfA8ssN7RE7nejQMTUZ99aFr6Ty6HjG4BD7CCTRN37EL0jN/ULwE0HQEi6ydDnOWkqJmpX8u9eWl3NX1PBGyuGckHtlbtquUngIxmWy0a0Q/S0XaDVBIHyQaJTAu+RZzZEVPOxkL61mjOFSdoJKdrBPUQECdGHVYOfaUlpQR1JzCJgpi65XVTktTdoiYgfEGC3Uks3LzeMua,iv:GLzRU3KgZeHxty8u3Zxe0NV3yxu5t2Gn1tj9UzQW68o=,tag:A1N/9hoXe/4YwIX6B9Y+/Q==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: [] + lastmodified: "2026-07-03T02:24:30Z" + mac: ENC[AES256_GCM,data:iVIEcb+8NkyVKZNokn+B63qWmQvqWDSaEzj75DLCbU+xUf4PEpMjJbjZOAQeZd3+OGfCE7WB/lg8I363Y5ibqb56i7UHXzZTk7tIbLWvEpZm8z+j6vmTQxuRsqwXJLauEr9bG7bxoC0XxeOxZPYHshUsBlbcFtd/aZbjANi/BBM=,iv:nUUc974hh5EAimQlYFCpwyezYW79GiUfgmcPRkeAH4Y=,tag:7okt3AK7uo/0ZVO3Q//3Hg==,type:str] + pgp: + - created_at: "2025-10-27T13:25:35Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMA32GcoRiZf6pAQ/+IKGAjEyWVLJbeKn+HXSWSx/ftofr+q6SawtUO7BZgUp8 + BpQuYK7/LZlsZ4Bq83/WZj0CgXDTZoVjxAoXgv1+If7tv0+tFg9l9tIuc4gAfOTn + DotvDygMRkY/kYWz+Pr+desDhma1YVxEglr/xFGmzM4v+u4Dd+ShAbwmnUsWHO0K + yucZyxIk2BeNVs0880u3pYmWwJNXckyS83kBZSP5SX+xbGpRgviGNnpk0kaYYGIr + SmAwoQi7NxVdkMd6XROV/rdL+xLjUBLPsIbVEDTqIVU6b1U7dUA6lNdKeDdqUt8B + VGpRa9AqN2HR49RnXXS56rDM2fZexVSjP6Tlc/2312zRiJzyRQhZQ/IiV84Qj0hF + KMb2nTszQr4QbhTFHD7dexnu21zQdwp1risJG9aJnBTcI/hzFiq52S8K3PDqEdlb + 50tuPoC0oG6AXXdfLycIc0VGhTSyNFr+N4VhToyRmGeP0CrUxe3XkwFiJiXxZH06 + tS/eNU5XNQ/w56sE2VVDip7OrPBwwDgiM+NaRC6lFRWKgndFmLDk9svzKzikjUCx + KrGqNQtBDUGvwTecDUXT3Ra6gZ3ibhUsyBnk1W14clm21n8ABdSOF84KgWq0rlHd + BsqIsbEqQuVdZ9i0pQO8i5YTsZU16/hV/o+U2bFlWnG0UnJDx6ubzWDX11rmvKHS + XAHTlLR/yto4S/I13bJvPG2R0hNBbvB3ZHHALnfS5lH/MuQXz6YCod7XmPcFfuKs + KJMKXusRuInXVbWWwHHKHx+GFnh/XJ/HXCx07amHyusWBWXjPqlIwKQcf1G2 + =Ko+N + -----END PGP MESSAGE----- + fp: 80DB2B4EF6CA4D9829C280605636BC0E08138A24 + - created_at: "2025-10-27T13:25:35Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMA87Wber4r4dGAQ/+JMA/0QWhmj9p4ib+o6BpC5UwBC8t3uKc+Nx3RtH1tbfu + mS4SgJNu6KR+exsH6Q3wsqxANmVKK6JjZXTiaHB2so/mT5FDctlimPbjpeqg8tcU + gec3e3jmh/QiFiIWWBy/kxORSvuQtcM8Fu5TyZiNYkV5mSmNN8YSzExjeAlOAkK1 + efBMIuoBS0QaRZqQuoqzqqnnP50FFGP9wi21FBi+ZPS2s8webYKMRoo5KySWKRh6 + PCFJ3ikJ+4n+Ln3Wq+KiWIVwzNWL9J/9bqDpIyKcA3niCvo6MGdq57AIFMV97BVs + HJ+GAKsfkZOqfASp4CEpQHZpWwc5D1T1sbq/OrPVwvKRkbcvqMM/hsqBJ9NTjR1l + 9trYk2XHr4mqYmrIsIHJCooxFyvScRsn7PA2ZSSfloLSyTZ0XaV6ueRagEN/2ny2 + 5OsSRVkCwctWWfXChj9ZGBbq+mDohobiNR09JGWrP6Xs9kHk1gvS7MgA7pbXMuad + RcKsdDFzu+Twz6nOajoBgeRl+atUmwY65e0T9wEqyCgRpBtkcK4IkblIixgVcDmb + w3GScohqQ+ehEcjTubAS5FiMs5+1B41oLtKQCooXoyfxTalwBtjaxdD+Em+nIVX7 + IiN6RpA5xcLvJyqB35LBdJinBtR24hB1S1zFJa6K7bPg8/tnfck56f3vftJZtIrS + UQEKBviA8UUfHn+oE7Gbt1ucoekkHI99smRG7rl3tWs2To6aXXD6dIHQHOwL52Qi + 3XohfZf2q4u/8a+uvwqHXNzihPDTIYNLbkjiQKrYGNsdUg== + =PTSf + -----END PGP MESSAGE----- + fp: 69F66EEA7AE36CCB77DDB8CA1BC39532FB4A2DBD + - created_at: "2025-10-27T13:25:35Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMA4hw3nPn82LyAQ//RpGs30EaSeC/rLuhGak5Yy40CZ+4R9pT/AHrizRIZxz6 + 5EodGYnRBzvX6i0Aik/M3mEHRSwIMwCfdF/tCpnjMp9exE1nu9ob32nrFtJnuRTP + E58tFRyf7Ib/1f8zMQNdId1kxtV2FY+H7fzBxrJNhiJLmpUSqzSORrPX80+ttT82 + tAxTRlk3+pYy/sgHsI4ADbGWbQUaYqxlYYbChyshT79cLlwJDqteIh83E/37Xu4s + byGdJKtzdM3PPzlhbSVvS1IGpp3xShO0/+t05Ubvk4hiJ2WKGWQjoHsNoAIsnHIR + IJX1VWAWNpiohzfuHcCPTChMHTCtINZAiCvlWfgXwMcX3ADZH0B4Gsi6rIeGb3L0 + fHk2nYdbO7i3wQrzD9qtLA0487gd8ZKh7bkxioAdzT8t7PrZpohYn6vxIYKnoRvM + FFH5RyFrGcIdxPIVQxECZcdwjWIKN1wH6KenEGe7HHAUrJH0unbr/Sbs8vEEMUFQ + vxG937vzQROVturJRdx3IE2Lzw6BrBYaGErnurVM5Po6b7jCAUwUIb82+/DYOzUY + fImO5tPWLPdSNfwdZTergdklmHPFgUt+tS0qFj9VTfLBxQmX2Dqh8WeipJxK57tD + KG7fuPk4iAVJB+oV03OQz4m/fHlMT7l3AuPnxhdkmRDIA5L4mrsqU7Zseq/OKEDS + UQEuHkX+QVDIBM/qp2UM17c9y/4W3/5FAw+R2ArZqBfjzKOTZ+LjhDNC6EDj4nmy + 1Y7BNS1dQuMBMendZrvEkDlEf7fi3o4ATCCLv6rzX4+2OQ== + =zNow + -----END PGP MESSAGE----- + fp: D1002590180DE371BCB455EAEFCD4ACD0B4D3F6E + - created_at: "2025-10-27T13:25:35Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAw01qfIBwY0JARAAq1NTq8zuUTFoJHzkq8y1hiSod0c5Qm2p0pCijIhdHmAD + f0VUcrNbvIx6LPZWI2fhGIYwoJgQrFxQCG9a/aYYzFHhcihPJwADI1TqPZ8b2D2/ + HKLQJW2AVokvppYkLXS+KOlEEHiOmLwzbGL5fkvwAWlzEt9x3PVUH14TCcWXfuqk + 1xz4I7gXfD/Ql23pIhRzaqENpT8bXplAEMh1jEc32inCQoNbUppA+B5Xt1Ox5XVG + kl3PW16wBM0+pNN4oQjMHZfQiiJBMxr44e8ZSeYWaSjBr8jVPshp3pUq3e93JmC0 + pD4NVtnITQpIyziKyA61qKmkwC6X/g6Zn4qEcwjtvhQqs1SIH2Yu5SNOHrfpQF7P + G8UoH5gX91L3ImuvDC4wxW8eDy5bMYqi7Pxy7u+Q5fTCN2VJAvDAyGwlO76kbKZQ + wGBzGNvJNE8BM4JyJRwPq0+TmQuM4wdZvtQ0M6ddJ1rqMB6Sz3GbTg1XIAU8/q5c + rFFZNrXpZH4PkMf2kOmU5eDKWCZa3c+HrLvkgYZxo66EFcbAUHajnTqdoEX1FaD/ + cDyl1GDw8VMzbBmqA6Ad0CJ12xxDalI7us6/6fS42vO8cwhsc3Kc9KQR/qWGLfqK + I48D5KkjzzQbgb0Yh0Qt2CNu+ooSmbVxiQm1JplfnCFZIMIEhPu+L0QxnxCF09vS + UQEe1uI5Pu/LRLR/HnxD80IQ/Kz2pgqzcezmYLtQXgO3tyfeO9kRz0hciNpBsGPT + Fx21MILxKR5ypjOyspEtt7gEvhQiCy8ixc58hj+STDeH6w== + =V6Be + -----END PGP MESSAGE----- + fp: B9F81F327CF5346860E85269D7AF98F214C59E4E + - created_at: "2025-10-27T13:25:35Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQEMAwDFOgk9tOQ+AQgAlRsVXPLBgH5iGDZ2RVI2Tz8vR26UoWTWtuj6ptpDR2F/ + BDMy6tohnRQiXXDHUgTm0otJaTAPNsw+90ryD1UXuGlzfYJv5tpTTTfywLEyhHtv + 5Y8T3bq7QwUFgM9e4LHVqt15b7ReJi9xkR/FoBH/4lh6NqE8Z6eD+BNszwR20aVC + u0dUwHPrROl6moPZIEvSwMPhCGbI5ZO2S6g+iGCsXNOLtCNtSVQw9Czf8JKMlCpD + EiyQOOwTjZZ6sGj7wDvxZdyW0K5LH7fsHms8V5+Pzpk+gLs9QKhYL3xa4KJg5Tg9 + 2rdqdfNQcECivtQH2wgVwPikQe5BzUx31cYwAvp/P9JcAbvqgXUG30AUrwojSi7d + RhfRVACpmUUoc+FsBjdUDux6GXkdkoIOxsJ3kPg37nJiwCzq3uXJptwoykjy+Por + rMTp5fr7JCMMAzC+mLyrpvr3Z77EffAEgULbTck= + =xfyc + -----END PGP MESSAGE----- + fp: 0600D41E1313E31016F7C55BF597BC0C5F22D1A2 + - created_at: "2025-10-27T13:25:35Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAz4a8AV36xppAQ//THNYUTc2CaY317l+48yibj89XMdC3O9S3XWIi0yQ88DT + MEYUA8j74oJwIBRX9H44nTpL0Jfbg4KqhdPSaS/ZXtgcxTrs3aym008C1Auf974D + OGWEG29ZavXiLj9N+Qy4k3jTGhXjyjp8qpis/wpR/yhLGRrFjorkNkJ0xf6bHBVg + DNpQSGfyY5cBSLCOSb51pZXJ1PBmWwKoIhtpZuFeV9OoayN42kDNTQdC1v+PICYK + PqHLcRB5e6mayPDmZu7YAzFui4yeTSpR/FDP54ckQuIZxN+Pw66n8ocRme/T/Mhz + H2o7RNwtGxgDRYyAz2puag+WFFZoo7tfbSXykSFEFBBQrP6OoQ3ZS5BuxuGSrwNS + GQ/AnzPHL4FfTBMz8tEfeOfwBWTiiZ0IVvEKa0YmGk7d3mlMGul08o5nTfgxI3W2 + YNp7E5TTEgpCfRHBUtH3hMIRwU0ecjTuM91/9kF8Up1UlDRABJ7DsQXZpEcH+SYG + XTLrhl0aHgrxSSmFv3I6JuiSuYQBpfmug6Ed9GJWrwMNFlr1QkblNIF1WEVbaQ6k + j37XL5VOe70tTM0FL3rqYCCuDKoJ2eUHGvJ2cjPekDxhQ/4ps8HwevG2v/oYY7VY + 7eSkC8QWs6JlWPZZJet5hmhRP2BF7LflqaPyAfyrR85kLH/ldS87A6kcIP2QDB/S + XAHKuYj9EjVjEBDjTIz0K2+ctHUR9TikwGan7lq5zFDdxVw6Qs46pGwP2FuXslSs + XK8WKviNRZ99UHHYpKAf3j8ztsKBaVlrM0f/ZUXsmIvPQIaHfLcqK/gmW76O + =Wzuw + -----END PGP MESSAGE----- + fp: 29C50D01122FDE78E257482DAA497EB2610A8435 + - created_at: "2025-10-27T13:25:35Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAxYzhHRfYJYtAQ//QHZYI5KPXJIeodQqPFJl8L/LnPa6ycOoNXuBngsYNBw3 + mT12bU/+zhiQd39Sh2xRhGGh3sK0SCCfarH/rjMhG/G+7hobFITh+jYMNuJO9kNd + SBii1HyvuwOkqSIYdVToiAKvmAXHQqAjNNCaHT2FBKZQSxq7eMC9/7GBEVBxiYlC + Xw9LoAAf6vrSJq/7nxokkAFYBwjH6H06lv0cOiCNuci3oKzE64toAo4zSwcj9yzQ + hFI4Q+Er62Ep8fMRxsy9NkxZNgLruPzssGxa/n4jk9t7fF9EQmyoeRVLUxNvr+w3 + W7sol2sxTupj2Bxtz9SH4h3Ubuey4HwyM5mD8n/euU+K1pO5aPBGf2oz1fbK3D0K + 9czZpSS/ehWjeytpjJCAmL6m2rRtd1IM29Bt9RNnRaL2hqUwliNVXfo2TKsilp0i + WL0f0ZpDElc9ttxWOFBir8fCCMFw6Oe/zoicEkxC7/W4iPyLTyHFZAlio2cBiV0I + 9dTtUQbKW1Yav30Y4Wg2SqlOEUXki5KkZ+GyWNRwVLePh3Ec0MbcXQRREesK0goq + pUV42dpAV52b5svPndZa8AJZe3RFbqIcFb08UNsDwFSN/ZGLH8geu+X7ROdq8XxN + yclqoVUv7Aq65kgaSqFszSYEG2+hoNVYpdYI/OTYFa7r7922fhqQSvPPUhm3JtDS + XAHE3c57G66vrGGwnO5J7Sy6DVS3TpFNONq0untKm9lj6M6QiME4P/tT9UIq5Uca + S0mO0LwZPogjfBgE1bFcUfk6I6Awo/6QupkKyqC7PWgsXnK0E1iJyN79Sukt + =cfzI + -----END PGP MESSAGE----- + fp: 9BE537027CB7467923E240FF2AED09371C121F91 + - created_at: "2025-10-27T13:25:35Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hF4DjS7VXZx9i8YSAQdAkcV1Mg7BYz1ISw2x8guADkVnYfZbcY1dxqt4M2QidkIw + Uws9+CDwZfSJSddGvcFkFTSUMKqjW2zY0zL12i+H8ixluWM6rJ2U49OnjAd7chip + 1GYBCQIQkV5CNAErwKJ7hD7y1Io4TGvsuRkYsm0L4CwR/9sy/T63eKHMqerVsRoN + 2FtEFaTV2Xc1iPq78GEJmBSsR8sZI1+EnAlZ0HRz1FUYsuL3/zRQnu2fucKkJBIs + axCTPk8EX80= + =8dXI + -----END PGP MESSAGE----- + fp: F93098C3D8ED15D0924A6DDAEB4E93A88660C55B + unencrypted_suffix: _unencrypted + version: 3.8.1 diff --git a/ansible/inventories/devnet-7/group_vars/all/all.yaml b/ansible/inventories/devnet-7/group_vars/all/all.yaml new file mode 100644 index 0000000..239c53c --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/all/all.yaml @@ -0,0 +1,493 @@ +--- +# ░██████╗░██╗░░░░░░█████╗░██████╗░░█████╗░██╗░░░░░  ██╗░░░██╗░█████╗░██████╗░░██████╗ +# ██╔════╝░██║░░░░░██╔══██╗██╔══██╗██╔══██╗██║░░░░░  ██║░░░██║██╔══██╗██╔══██╗██╔════╝ +# ██║░░██╗░██║░░░░░██║░░██║██████╦╝███████║██║░░░░░  ╚██╗░██╔╝███████║██████╔╝╚█████╗░ +# ██║░░╚██╗██║░░░░░██║░░██║██╔══██╗██╔══██║██║░░░░░  ░╚████╔╝░██╔══██║██╔══██╗░╚═══██╗ +# ╚██████╔╝███████╗╚█████╔╝██████╦╝██║░░██║███████╗  ░░╚██╔╝░░██║░░██║██║░░██║██████╔╝ +# ░╚═════╝░╚══════╝░╚════╝░╚═════╝░╚═╝░░╚═╝╚══════╝  ░░░╚═╝░░░╚═╝░░╚═╝╚═╝░░╚═╝╚═════╝░ + +domain: ethpandaops.io +network_subdomain: "{{ ethereum_network_name }}.{{ domain }}" +network_server_subdomain: srv.{{ network_subdomain }} +server_fqdn: "{{ inventory_hostname }}.{{ network_server_subdomain }}" +network_iteration: "{{ ethereum_network_name.split('-')[1:] | join('-') }}" + +ethereum_network_id: >- + {{ (lookup('file', eth_testnet_config_local_dir_src + '/genesis.json') | from_json).config.chainId }} +ethereum_network_deposit_contract: >- + {{ lookup('file', eth_testnet_config_local_dir_src + '/deposit_contract.txt') }} +ethereum_network_deposit_contract_block: >- + {{ lookup('file', eth_testnet_config_local_dir_src + '/deposit_contract_block.txt') }} + +ethereum_node_rpc_prefix: rpc- # prefix for rpc URLs +ethereum_node_beacon_prefix: bn- # prefix for beacon URLs +ethereum_node_rcp_hostname: "{{ ethereum_node_rpc_prefix }}{{ server_fqdn }}" +ethereum_node_beacon_hostname: "{{ ethereum_node_beacon_prefix }}{{ server_fqdn }}" + +primary_bootnode: bootnode-1 + +### Checkpoint sync specific for Ansible & Kubernetes +checkpoint_sync_node: "{{ primary_bootnode }}" +# Enable once the network is past genesis and checkpointz is serving. +ethereum_node_cl_checkpoint_sync_enabled: false + +checkpoint_sync_url: https://checkpoint-sync.{{ ethereum_network_name }}.{{ domain }} +# checkpoint_sync_url: https://{{ secret_nginx_shared_basic_auth.name }}:{{ secret_nginx_shared_basic_auth.password }}@{{ ethereum_node_beacon_prefix }}{{ checkpoint_sync_node }}.{{ ethereum_network_name }}.{{ domain }} +checkpoint_sync_url_kube: https://@{{ + ethereum_node_beacon_prefix }}{{ checkpoint_sync_node }}.{{ ethereum_network_name }}.{{ domain }} + +# ██████╗░░█████╗░██╗░░░░░███████╗  ██╗░░░██╗░█████╗░██████╗░░██████╗ +# ██╔══██╗██╔══██╗██║░░░░░██╔════╝  ██║░░░██║██╔══██╗██╔══██╗██╔════╝ +# ██████╔╝██║░░██║██║░░░░░█████╗░░  ╚██╗░██╔╝███████║██████╔╝╚█████╗░ +# ██╔══██╗██║░░██║██║░░░░░██╔══╝░░  ░╚████╔╝░██╔══██║██╔══██╗░╚═══██╗ +# ██║░░██║╚█████╔╝███████╗███████╗  ░░╚██╔╝░░██║░░██║██║░░██║██████╔╝ +# ╚═╝░░╚═╝░╚════╝░╚══════╝╚══════╝  ░░░╚═╝░░░╚═╝░░╚═╝╚═╝░░╚═╝╚═════╝░ + +# role: ethpandaops.general.ethereum_genesis +ethereum_genesis_generator_container_image: "{{ default_tooling_images.ethereum_genesis_generator }}" +ethereum_genesis_generator_version: "{{ ethereum_genesis_generator_container_image.split(':')[-1] }}" +ethereum_genesis_network_seed: glamsterdam-devnet-7 +ethereum_genesis_chain_id: 70{{ 99999999 | random(start=10000000, seed=ethereum_genesis_network_seed) }} +ethereum_genesis_fork_version_suffix: "{{ 999999 | random(start=100000, seed=ethereum_genesis_network_seed) }}" +ethereum_genesis_generator_output_dir: ../network-configs/{{ network_iteration }} +# Pin at launch. While unset, the role default derives it from the relative +# command below (now +45 minutes) at playbook runtime. +# ethereum_genesis_timestamp: "1782386940" +ethereum_genesis_timedelay: 60 +ethereum_genesis_timestamp_relative_cmd: + Linux: date +%s -d '+45 minutes' + Darwin: date -v +35M +%s +shadowfork_height: 0 +ethereum_genesis_mnemonic: "{{ secret_genesis_mnemonic }}" +ethereum_genesis_generator_config_files: + cl/config.yaml: "{{ lookup('ansible.builtin.url', 'https://raw.githubusercontent.com/ethpandaops/ethereum-genesis-generator/v{{ethereum_genesis_generator_version}}/config-example/cl/config.yaml', + split_lines=false) }}" # noqa yaml[line-length] + cl/mnemonics.yaml: "{{ lookup('ansible.builtin.url', 'https://raw.githubusercontent.com/ethpandaops/ethereum-genesis-generator/v{{ethereum_genesis_generator_version}}/config-example/cl/mnemonics.yaml', + split_lines=false) }}" # noqa yaml[line-length] + el/genesis-config.yaml: "{{ lookup('ansible.builtin.url', 'https://raw.githubusercontent.com/ethpandaops/ethereum-genesis-generator/v{{ethereum_genesis_generator_version}}/config-example/el/genesis-config.yaml', + split_lines=false) }}" # noqa yaml[line-length] + # Network identity and fork schedule only — every spec parameter not listed + # here follows the egg defaults (devnet-6 overrode churn/withdrawability/ + # balance params; those are gone on purpose). + values.env: |- + export CHAIN_ID="{{ ethereum_genesis_chain_id }}" + export EL_AND_CL_MNEMONIC="{{ ethereum_genesis_mnemonic }}" + export NUMBER_OF_VALIDATORS=3000 + export GENESIS_FORK_VERSION="0x10{{ ethereum_genesis_fork_version_suffix }}" + export ALTAIR_FORK_VERSION="0x20{{ ethereum_genesis_fork_version_suffix }}" + export BELLATRIX_FORK_VERSION="0x30{{ ethereum_genesis_fork_version_suffix }}" + export CAPELLA_FORK_VERSION="0x40{{ ethereum_genesis_fork_version_suffix }}" + export DENEB_FORK_VERSION="0x50{{ ethereum_genesis_fork_version_suffix }}" + export ELECTRA_FORK_VERSION="0x60{{ ethereum_genesis_fork_version_suffix }}" + export FULU_FORK_VERSION="0x70{{ ethereum_genesis_fork_version_suffix }}" + export GLOAS_FORK_VERSION="0x80{{ ethereum_genesis_fork_version_suffix }}" + export BPO_1_EPOCH="1" + export BPO_2_EPOCH="2" + export GLOAS_FORK_EPOCH="30" + export GENESIS_TIMESTAMP={{ ethereum_genesis_timestamp }} + export GENESIS_DELAY={{ ethereum_genesis_timedelay }} + export EL_PREMINE_ADDRS='{"0x9a97ee9d32a0d68406e32b34c92afb81ce2bc467": {"balance": "100000ETH"}, "0x107781Bc6FA8f66B843f4216fd6D5862D3aa4fcd": {"balance": "100000ETH"}, "0x118A1030baf8Fa56f9B8235C163d3112FA8c3b89": {"balance": "100000ETH"}}' + export DEPLOY_EIP8282_CONTRACTS=false + +# 0x9a97ee9d32a0d68406e32b34c92afb81ce2bc467: 100000ETH - arbitrum +# 0x107781Bc6FA8f66B843f4216fd6D5862D3aa4fcd: 100000ETH - optimism +# 0x118A1030baf8Fa56f9B8235C163d3112FA8c3b89: 100000ETH - lido + +# DEPLOY_EIP8282_CONTRACTS=false: the builder deposit/exit contracts are NOT in +# genesis this time — deploy them manually on the live network before the gloas +# fork (epoch 30). + +ethereum_genesis_validator_keys_output_dir: "{{ ansible_inventory_sources[0] | dirname }}/files/validator_keys" +ethereum_genesis_validator_bls_change_execution_address: "{{ ethereum_node_cl_validator_fee_recipient }}" +ethereum_genesis_validator_keyranges: >- + {%- set ns = namespace() -%} + {%- set ns.ethereum_genesis_validator_keyranges = {} -%} + {%- for host in groups['all'] -%} + {%- if hostvars[host].validator_start is defined and hostvars[host].validator_end is defined -%} + {%- set v = {'start':hostvars[host].validator_start, 'end': hostvars[host].validator_end } -%} + {%- set _ = ns.ethereum_genesis_validator_keyranges.update({host: v}) -%} + {%- endif -%} + {%- endfor -%} + {{ ns.ethereum_genesis_validator_keyranges }} + +# role: ethpandaops.general.bootstrap +bootstrap_default_user_authorized_keys_plain: + - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDbboxOo0jyL3DNxqZ6UTEnZPEzPDPnujEYaClqNWSLWkphczHKAnJPkrwbAWB4JbJKjsAJ5kn53f10KPnUyZvJ5Jn8Rpf7RM7+56MYaBg84gVoA2KeIYxUa7h8neY7J61Galp0c6cOK+hp1lPsoiBSdCW/Rtbv6ALCcVe+4+uCW5FRoJcNRJfGRLRnjh1pw57HQw9O55mf319s4rVUq4umznQ0CciEx3rVMtXf4xjIZDZAhNpGaBh8AtHauaMZCOGociAIquYYqoSQnnmnOBiduRa5OkvGZomgybNQivlYboDeF6sQ71KVzRXSI+mxCYbSp246lqSdQtQsjFA54NYl/qWgAql0uqCqsZidW+XBjquyItRl2Rfzzy5Fk/gMOAJXHQYp4POfgFbqtxjWpfnuOKqW/1IGWcIt2g016effUqGgj/oePX0g+duFdszSKK773rJBySgafFF6XWNqagrLmE4LUGC+6P3oxzYTSFGeUVA21OayL+K40XPpJti5zns= + # devops-eth2-shared" + - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDWh9NW66VD4BPKETNyZeZrGN1f7G6dkihW3eAc7cbJPFQGIpnWc2tGq5o13vWW+SoCh16nkYM2oak+PJQxXYTiQnrMJSmSFd7E0DmdcoKadGJEnfosrH++aOZf/eVLe5q3E9NQFVSdOPo1MCRRTuZxPkuMxS6QikW3otWrA3F2vFgmYyki3Cy8huQzHKUZGicividYcUSFTydR2L0oWUNve3FyqMQQQPnfaJ1RvrkeGtdhRSAxa6L0jzgRK7fjpUyhKOofr7kCKARGELRRiB9QikRAoHU2/D/2jtJjKlTCJxArzXyDF2IcQCco+5Oe9x4c7Xch32dbscJSmjaAvsxRnu7GEFCS7b6kKGvwcoq5vJzvp3RBBR7Mosxv6pcM/q7Z4RhXOFVFFiPVl1dqkqSPkUrHwg8LtWOxC+GAl36vxhHLdDEV/RhbSAzO6SfYEWYGH1w7u4oiy2XAT2cNCO0j0tSHS5chX+d7TzwAbBE2HuPL84GVGHZG875hmiE+Dok= + # github-actions-ci" +# Per-client team SSH rosters are fetched at runtime from the coredevs registry +# (see each client group_vars file). https://github.com/ethpandaops/coredevs +coredevs_api_url: https://coredevs.analytics.production.platform.ethpandaops.io + +# ethpandaops core team, sourced from the coredevs ethpandaops team. Handles not +# in the coredevs team go in the extra list. +github_all_extra_users: + - bharath-123 +bootstrap_default_user_authorized_keys_github_all: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/ethpandaops?format=txt') | select | list) + + github_all_extra_users }} + +bootstrap_default_user_authorized_keys_github: > + {{ + (bootstrap_default_user_authorized_keys_github_all | default([])) + + (bootstrap_default_user_authorized_keys_github_team_el | default([])) + + (bootstrap_default_user_authorized_keys_github_team_cl | default([])) + }} + +node_exporter_container_image: "{{ default_tooling_images.node_exporter }}" +prometheus_container_image: "{{ default_tooling_images.prometheus }}" +vector_container_image: "{{ default_tooling_images.vector }}" +json_rpc_snooper_container_image: "{{ default_tooling_images.json_rpc_snooper }}" + +# role: ethpandaops.general.ethereum_node +ethereum_node_images_always_pull: true +ethereum_node_metrics_exporter_enabled: true +ethereum_node_xatu_sentry_enabled: true +ethereum_node_cl_validator_enabled: "{{ validator_start is defined and validator_end is defined }}" +ethereum_node_cl_validator_fee_recipient: "0xf97e180c050e5Ab072211Ad2C213Eb5AEE4DF134" +ethereum_node_cl_ports_p2p_tcp: 9000 +ethereum_node_cl_ports_p2p_udp: 9000 +ethereum_node_cl_ports_http_beacon: 5052 +ethereum_node_cl_ports_metrics: 5054 +ethereum_node_el_ports_p2p_tcp: 30303 +ethereum_node_el_ports_p2p_udp: 30303 +ethereum_node_el_ports_http_rpc: 8545 +ethereum_node_el_ports_ws_rpc: 8546 +ethereum_node_el_ports_engine: 8551 +ethereum_node_el_ports_metrics: 6060 +ethereum_node_ipv6_enabled: true +ethereum_node_el_public_ipv6: "{{ host_ipv6_prefix | ansible.utils.ipaddr(service_ipv6_offsets.execution) | ansible.utils.ipaddr('address') if (docker_network_enable_ipv6 + | bool) else '' }}" +ethereum_node_cl_public_ipv6: "{{ host_ipv6_prefix | ansible.utils.ipaddr(service_ipv6_offsets.beacon) | ansible.utils.ipaddr('address') if (docker_network_enable_ipv6 + | bool) else '' }}" +ethereum_node_validator_public_ipv6: "{{ host_ipv6_prefix | ansible.utils.ipaddr(service_ipv6_offsets.validator) | ansible.utils.ipaddr('address') if (docker_network_enable_ipv6 + | bool) else '' }}" +ethereum_node_snooper_rpc_public_ipv6: "{{ host_ipv6_prefix | ansible.utils.ipaddr(service_ipv6_offsets.snooper_rpc) | ansible.utils.ipaddr('address') if (docker_network_enable_ipv6 + | bool) else '' }}" +ethereum_node_snooper_engine_public_ipv6: "{{ host_ipv6_prefix | ansible.utils.ipaddr(service_ipv6_offsets.snooper_engine) | ansible.utils.ipaddr('address') if (docker_network_enable_ipv6 + | bool) else '' }}" +ethereum_node_skip_cleanup: true +ethereum_node_docker_watchtower_enabled: true +ethereum_node_docker_watchtower_containers_list: + - execution + - beacon + - validator + - xatu-sentry +docker_watchtower_container_additional_args: + - --interval=900 + - --stop-timeout=300s + - --include-restarting +ethereum_node_json_rpc_snooper_engine_enabled: true +ethereum_node_json_rpc_snooper_engine_name: snooper-engine +ethereum_node_json_rpc_snooper_engine_port: 8561 +ethereum_node_json_rpc_snooper_engine_public_port: 8961 +ethereum_node_json_rpc_snooper_engine_container_env: + SNOOPER_API_PORT: "{{ ethereum_node_json_rpc_snooper_engine_public_port | quote }}" + SNOOPER_API_AUTH: "{{ secret_nginx_shared_basic_auth.name }}:{{ secret_nginx_shared_basic_auth.password }}" + +# role: ethpandaops.general.generate_kubernetes_config +gen_kubernetes_config_dora_execution_snooper_port: 8961 +gen_kubernetes_config_dora_frontend_rainbowkit_id: 15fe4ab4d5c0bcb6f0dc7c398301ff0e +gen_kubernetes_config_dora_execution_endpoints_url: https://raw.githubusercontent.com/ethpandaops/{{ devnet_name }}-devnets/refs/heads/master/kubernetes/{{ + network_iteration }}/dora/endpoints-el.yaml +gen_kubernetes_config_dora_consensus_endpoints_url: https://raw.githubusercontent.com/ethpandaops/{{ devnet_name }}-devnets/refs/heads/master/kubernetes/{{ + network_iteration }}/dora/endpoints-cl.yaml +gen_kubernetes_config_dora_api_secret: "{{ secret_dora_api_key }}" + +# role: ethpandaops.general.xatu_sentry +xatu_sentry_container_image: "{{ default_tooling_images.xatu_sentry }}" +xatu_sentry_config_name: "{{ ethereum_network_name }}-{{ inventory_hostname }}" +# Per-devnet xatu pipeline (mca-xatu-devnets in platform): the ingest host is +# derived from the network name so new devnets need no wiring here. +xatu_sentry_config_server_address: "{{ ethereum_network_name }}-ingest.xatu.ethpandaops.io:443" +xatu_sentry_config_server_auth_user: "{{ secret_xatu_sentry.user }}" +xatu_sentry_config_server_auth_password: "{{ secret_xatu_sentry.password }}" +xatu_sentry_config_network_name_override: "{{ ethereum_network_name }}" + +# Full config override: adds the Gloas SSE topics, which the sentry's default +# subscription set deliberately excludes. +xatu_sentry_config: | + logging: "info" + metricsAddr: ":9090" + name: "{{ xatu_sentry_config_name }}" + ntpServer: time.google.com + ethereum: + beaconNodeAddress: {{ xatu_sentry_config_beacon_uri }} + overrideNetworkName: {{ xatu_sentry_config_network_name_override }} + beaconSubscriptions: + - attestation + - single_attestation + - block + - block_gossip + - chain_reorg + - finalized_checkpoint + - head + - voluntary_exit + - contribution_and_proof + - blob_sidecar + - data_column_sidecar + - execution_payload + - execution_payload_gossip + - execution_payload_available + - execution_payload_bid + - payload_attestation_message + - proposer_preferences + outputs: + - name: grpc + type: xatu + config: + address: {{ xatu_sentry_config_server_address }} + tls: {{ xatu_sentry_config_server_tls_enabled }} + maxQueueSize: 100000 + workers: 5 + headers: + authorization: "Basic {{ (xatu_sentry_config_server_auth_user + ":" + xatu_sentry_config_server_auth_password) | b64encode }}" + +# role: ethpandaops.general.ethereum_metrics_exporter +ethereum_metrics_exporter_container_image: "{{ default_tooling_images.ethereum_metrics_exporter }}" +ethereum_metrics_exporter_container_user: root +ethereum_metrics_exporter_container_volumes: + - "{{ ethereum_metrics_exporter_dir_config }}:/config:ro" + - /var/run/docker.sock:/var/run/docker.sock:ro + - /data:/data:ro +ethereum_metrics_exporter_config: | + consensus: + enabled: true + url: "{{ ethereum_metrics_exporter_cl_endpoint }}" + name: "consensus-client" + execution: + enabled: true + url: "{{ ethereum_metrics_exporter_el_endpoint }}" + name: "execution-client" + modules: + - "eth" + - "net" + - "web3" + - "txpool" + docker: + enabled: true + endpoint: "unix:///var/run/docker.sock" + interval: "10s" + containers: + - name: "execution" + type: "execution" + filesystem: + enabled: true + volumes: + - name: "*" + monitor: true + - path: "/execution-auth.jwt" + monitor: false + - path: "/network-config" + monitor: false + - name: "beacon" + type: "consensus" + filesystem: + enabled: true + volumes: + - name: "*" + monitor: true + - path: "/execution-auth.jwt" + monitor: false + - path: "/network-config" + monitor: false + +# role: eth_testnet_config +eth_testnet_config_dir: /data/ethereum-network-config/metadata +eth_testnet_config_local_dir_enabled: true +eth_testnet_config_local_dir_src: "{{ ethereum_genesis_generator_output_dir }}/metadata/" + +# role: gen_basic_auth_nginx +gen_basic_auth_nginx_name: "{{ secret_nginx_shared_basic_auth.name }}" +gen_basic_auth_nginx_password: "{{ secret_nginx_shared_basic_auth.password }}" +docker_nginx_proxy_container_image: "{{ default_tooling_images.nginx_proxy }}" +docker_nginx_proxy_docker_gen_container_image: "{{ default_tooling_images.nginx_proxy_gen }}" +docker_nginx_proxy_acme_companion_container_image: "{{ default_tooling_images.nginx_proxy_acme }}" + +# role: ethpandaops.general.docker_nginx_proxy +docker_nginx_proxy_public_ipv6: "{{ host_ipv6_prefix | ansible.utils.ipaddr(service_ipv6_offsets.nginx_proxy) | ansible.utils.ipaddr('address') if (docker_network_enable_ipv6 + | bool) else '' }}" +docker_nginx_proxy_container_name: nginx-proxy +docker_nginx_proxy_default_email: certs@{{ domain }} +docker_nginx_proxy_docker_gen_container_name: nginx-proxy-gen +docker_nginx_proxy_docker_gen_container_env: + RESOLVERS: 1.1.1.1 + ENABLE_IPV6: "true" +docker_nginx_proxy_acme_companion_enabled: false +docker_nginx_proxy_acme_companion_container_name: nginx-proxy-acme +docker_nginx_proxy_acme_companion_container_env: + DEFAULT_EMAIL: "{{ docker_nginx_proxy_default_email }}" + NGINX_PROXY_CONTAINER: "{{ docker_nginx_proxy_container_name }}" + NGINX_DOCKER_GEN_CONTAINER: "{{ docker_nginx_proxy_docker_gen_container_name }}" + ACME_CA_URI: https://acme.zerossl.com/v2/DV90 + ACME_EAB_KID: "{{ secret_zerossl.ACME_EAB_KID }}" + ACME_EAB_HMAC_KEY: "{{ secret_zerossl.ACME_EAB_HMAC_KEY }}" +docker_nginx_proxy_container_networks: >- + {{ [{'name': 'shared'} | combine({'ipv6_address': docker_nginx_proxy_public_ipv6} if (docker_nginx_proxy_public_ipv6 | default('') | length > 0) else {})] }} +docker_nginx_proxy_docker_gen_container_networks: [] +docker_nginx_proxy_acme_companion_container_networks: "{{ docker_networks_shared }}" +docker_nginx_proxy_acme_monitor_container_networks: "{{ docker_networks_shared }}" +docker_nginx_proxy_acme_monitor_enabled: false +docker_nginx_proxy_datadir: /opt/nginx-proxy +docker_nginx_proxy_container_volumes: + - "{{ docker_nginx_proxy_datadir }}/conf:/etc/nginx/conf.d" + - "{{ docker_nginx_proxy_datadir }}/vhost:/etc/nginx/vhost.d" + - "{{ docker_nginx_proxy_datadir }}/certs:/etc/nginx/certs:ro" + - html:/usr/share/nginx/html + - "{{ docker_nginx_proxy_datadir }}/htpasswd:/etc/nginx/htpasswd:ro" +docker_nginx_proxy_cert_loader_container_image: "{{ default_tooling_images.nginx_proxy_cert_loader }}" +docker_nginx_proxy_cert_linker_container_image: "{{ default_tooling_images.nginx_proxy_cert_linker }}" +docker_nginx_proxy_cert_loader_container_networks: [] +docker_nginx_proxy_cert_linker_container_networks: [] +docker_nginx_proxy_wildcard_cert: "{{ network_server_subdomain }}" +docker_nginx_proxy_wildcard_cert_url: http://cert.{{ network_server_subdomain }}/{{ network_server_subdomain }}-latest.tar.enc +docker_nginx_proxy_wildcard_cert_psk: "{{ secret_cert_encryption_psk }}" + +# OTLP egress to the prod analytics gateway (Vector ships container logs here). +# Ingress identity (auth username + ingress_user tag) is the current devnet name +# rather than secret_loki.username: the sops username drifts stale between devnet +# iterations, while the gateway only validates the (constant) password — so using +# the network name keeps log attribution correct no matter what sops holds. +otlp_endpoint: https://otlp.analytics.production.platform.ethpandaops.io +otlp_deployment_env: production + +# role: ethpandaops.general.vector +vector_config: | + # Docker container logs (clean per-container metadata straight from the Docker API) + [sources.in] + type = "docker_logs" + exclude_containers = [ + "{{ vector_container_name }}", + "ethereum-metrics-exporter", + "nginx-proxy", + "node_exporter", + "prometheus", + "snooper-", + ] + + # Shape docker_logs events into an OTLP resourceLogs envelope with full metadata. + [transforms.otel_shape] + type = "remap" + inputs = ["in"] + source = ''' + ts_ns = to_string(to_unix_timestamp(now(), unit: "nanoseconds")) + if is_timestamp(.timestamp) { + ts_ns = to_string(to_unix_timestamp!(.timestamp, unit: "nanoseconds")) + } + msg = string(.message) ?? "" + ctr = string(.container_name) ?? "unknown" + img = string(.image) ?? "" + strm = string(.stream) ?? "stdout" + + # Extract the source log level: JSON-structured lines (.level / .severity) + # first, then logfmt (level=xxx), then a level token near the start of a + # plain-text line. Client formats vary (lighthouse truncates to 4 chars: + # DEBG/ERRO/CRIT), so the text matcher allows a union of forms. SeverityText + # keeps the source's exact text; only SeverityNumber is normalised to the + # OTel scale (https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber). + # Unrecognised lines are left unset rather than guessed. + sevtext = "" + if starts_with(msg, "{") { + j = parse_json(msg) ?? {} + if is_string(j.level) { + sevtext = string!(j.level) + } else if is_string(j.severity) { + sevtext = string!(j.severity) + } + } + if sevtext == "" { + lm = parse_regex(msg, r'(?i)\b(?:level|lvl|severity)="?(?P[a-z]+)') ?? {} + if is_string(lm.l) { + sevtext = string!(lm.l) + } else { + hm = parse_regex(truncate(msg, 48), r'(?i)\b(?PTRACE|DEBUG|DEBG|DBUG|NOTICE|INFO|WARNING|WARN|ERROR|ERRO|CRITICAL|CRIT|FATAL|PANIC)\b') ?? {} + if is_string(hm.l) { + sevtext = string!(hm.l) + } + } + } + lvl = upcase(sevtext) + sevnum = 0 + if lvl == "TRACE" { + sevnum = 1 + } else if lvl == "DEBUG" || lvl == "DEBG" || lvl == "DBUG" { + sevnum = 5 + } else if lvl == "INFO" || lvl == "INFOR" || lvl == "NOTICE" { + sevnum = 9 + } else if lvl == "WARN" || lvl == "WARNING" { + sevnum = 13 + } else if lvl == "ERROR" || lvl == "ERRO" { + sevnum = 17 + } else if lvl == "CRIT" || lvl == "CRITICAL" { + sevnum = 18 + } else if lvl == "FATAL" || lvl == "PANIC" { + sevnum = 21 + } + + attrs = [ + {"key": "service.name", "value": {"stringValue": ctr}}, + {"key": "container.name", "value": {"stringValue": ctr}}, + {"key": "container.image.name", "value": {"stringValue": img}}, + {"key": "deployment.environment", "value": {"stringValue": "{{ otlp_deployment_env }}"}}, + {"key": "forwarder", "value": {"stringValue": "vector"}}, + {"key": "ingress_user", "value": {"stringValue": "{{ ethereum_network_name }}"}}, + {"key": "network", "value": {"stringValue": "{{ ethereum_network_name }}"}}, + {"key": "testnet", "value": {"stringValue": "{{ ethereum_network_name }}"}}, + {"key": "instance", "value": {"stringValue": "{{ inventory_hostname }}"}}, + {"key": "host.name", "value": {"stringValue": "{{ inventory_hostname }}"}} + ] + {%- if ethereum_node_cl is defined +%} + attrs = push(attrs, {"key": "ethereum_cl", "value": {"stringValue": "{{ ethereum_node_cl }}"}}) + {%- endif +%} + {%- if ethereum_node_el is defined +%} + attrs = push(attrs, {"key": "ethereum_el", "value": {"stringValue": "{{ ethereum_node_el }}"}}) + {%- endif +%} + . = { + "resource_log": { + "resource": {"attributes": attrs}, + "scopeLogs": [{ + "scope": {"name": "{{ ethereum_network_name }}-vector"}, + "logRecords": [{ + "timeUnixNano": ts_ns, + "severityNumber": sevnum, + "severityText": sevtext, + "body": {"stringValue": msg}, + "attributes": [{"key": "stream", "value": {"stringValue": strm}}] + }] + }] + } + } + ''' + + # Batch many shaped log events into one OTLP envelope (reduce works on logs). + [transforms.batch_envelope] + type = "reduce" + inputs = ["otel_shape"] + expire_after_ms = 30000 + end_every_period_ms = 5000 + max_events = 500 + merge_strategies.resource_log = "array" + + [transforms.finalize_envelope] + type = "remap" + inputs = ["batch_envelope"] + source = ''' + . = {"resourceLogs": .resource_log} + ''' + + [sinks.otlp_logs] + type = "opentelemetry" + inputs = ["finalize_envelope"] + [sinks.otlp_logs.protocol] + type = "http" + uri = "{{ otlp_endpoint }}/v1/logs" + method = "post" + encoding.codec = "otlp" + auth.strategy = "basic" + auth.user = "{{ ethereum_network_name }}" + auth.password = "{{ secret_loki.password }}" + # One event here is already a full OTLP envelope (built by the reduce above). + # max_events MUST be 1 — OTLP/HTTP allows one envelope per request. + batch.max_events = 1 + batch.timeout_secs = 5 diff --git a/ansible/inventories/devnet-7/group_vars/all/images.yaml b/ansible/inventories/devnet-7/group_vars/all/images.yaml new file mode 100644 index 0000000..cc233af --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/all/images.yaml @@ -0,0 +1,68 @@ +--- +default_ethereum_client_images: + ### Consensus layer clients + lighthouse: ethpandaops/lighthouse:glamsterdam-devnet-7 + lodestar: ethpandaops/lodestar:glamsterdam-devnet-7 + nimbus: ethpandaops/nimbus-eth2:glamsterdam-devnet-7 + prysm: ethpandaops/prysm-beacon-chain:glamsterdam-devnet-7 + prysm_validator: ethpandaops/prysm-validator:glamsterdam-devnet-7 + teku: ethpandaops/teku:glamsterdam-devnet-7 + grandine: ethpandaops/grandine:glamsterdam-devnet-7 + ### Execution layer clients + besu: ethpandaops/besu:glamsterdam-devnet-7 + geth: ethpandaops/geth:glamsterdam-devnet-7 + erigon: ethpandaops/erigon:glamsterdam-devnet-7 + nethermind: ethpandaops/nethermind:glamsterdam-devnet-7 + reth: ethpandaops/reth:glamsterdam-devnet-7 + nimbusel: ethpandaops/nimbus-eth1:glamsterdam-devnet-7 + ethrex: ethpandaops/ethrex:glamsterdam-devnet-7 + +default_tooling_images: + mev_boost: ethpandaops/mev-boost:develop + mev_builder: ethpandaops/reth-rbuilder:develop + mev_relay: ethpandaops/mev-boost-relay:main + buildoor: ethpandaops/buildoor:main-latest + authenticatoor: ethpandaops/service-authenticatoor:main-latest + xatu_sentry: ethpandaops/xatu:glamsterdam-devnet-7 + xatu_cannon: ethpandaops/xatu:latest + xatu_mimicry: ethpandaops/xatu:latest + xatu_cl_mimicry: ethpandaops/xatu:latest + xatu_relay_monitor: ethpandaops/xatu:latest + ethereum_metrics_exporter: ethpandaops/ethereum-metrics-exporter:latest + tx_fuzz: ethpandaops/tx-fuzz:latest + forkmon: skylenet/nodemonitor:darkmode + forky: ethpandaops/forky:latest + fauceth: skylenet/fauceth:fix_fee_estimation + powfaucet: pk910/powfaucet:v2.5.0 + powfaucet_agents: ghcr.io/qu0b/powfaucet:v2.5.0-agent.4 + homepage: ethpandaops/ethereum-testnet-homepage:latest + checkpointz: ethpandaops/checkpointz:gloas-latest + blockscout: blockscout/blockscout:latest + blockscout_frontend: docker.ethquokkaops.io/gh/blockscout/frontend:latest + beacon_metrics_gazer: dapplion/beacon-metrics-gazer:latest + eth_fauceth: chainflag/eth-faucet:latest + blobscan: blossomlabs/blobscan:latest + blobscan_indexer: blossomlabs/blobscan-indexer:latest + dora: ethpandaops/dora:master-latest + dugtrio: ethpandaops/dugtrio:bbusa-endpoint-failover + ethereum_genesis_generator: ethpandaops/ethereum-genesis-generator:6.1.3 + tracoor: ethpandaops/tracoor:latest + ncli: status-im/nimbus-eth2:unstable + lcli: ethpandaops/lighthouse:unstable + zcli: electra + assertoor: ethpandaops/assertoor:master-latest + erpc: docker.ethquokkaops.io/gh/erpc/erpc:latest + prometheus: prom/prometheus:v2.40.7 + node_exporter: prom/node-exporter:v1.5.0 + cl_bootnode: protolambda/eth2-bootnode:cleanup + json_rpc_snooper: ethpandaops/rpc-snooper:latest + nginx_proxy: nginx:alpine + nginx_proxy_gen: nginxproxy/docker-gen + nginx_proxy_acme: nginxproxy/acme-companion + nginx_proxy_cert_loader: ethpandaops/debian-docker:latest + nginx_proxy_cert_linker: nginxproxy/docker-gen + vector: timberio/vector:0.56.0-alpine + spamoor: ethpandaops/spamoor:master-latest + blobber: ethpandaops/blobber:latest + syncoor_web: docker.ethquokkaops.io/gh/ethpandaops/syncoor-web:master + syncoor_server: docker.ethquokkaops.io/gh/ethpandaops/syncoor:master diff --git a/ansible/inventories/devnet-7/group_vars/besu.yaml b/ansible/inventories/devnet-7/group_vars/besu.yaml new file mode 100644 index 0000000..acad65c --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/besu.yaml @@ -0,0 +1,85 @@ +--- +# role: ethpandaops.general.bootstrap +besu_extra_github_users: [] # handles not yet in the coredevs registry +bootstrap_default_user_authorized_keys_github_team_el: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/besu?format=txt') | select | list) + + besu_extra_github_users }} + +# role: ethpandaops.general.ethereum_node +ethereum_node_el: besu +# role: ethpandaops.general.besu +besu_container_name: execution +besu_container_image: "{{ default_ethereum_client_images.besu }}" +besu_container_env: + VIRTUAL_HOST: "{{ ethereum_node_rcp_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_el_ports_http_rpc | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_rcp_hostname }}" +besu_container_volumes: + - "{{ besu_datadir }}:/data" + - "{{ besu_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +besu_container_command_extra_args: + - --genesis-file=/network-config/besu.json + - --bootnodes={{ ethereum_el_bootnodes | join(',') }} + - --rpc-http-api=ADMIN,DEBUG,ETH,MINER,NET,TRACE,TXPOOL,WEB3 + - --sync-mode=FULL + - --data-storage-format=BONSAI + - --bonsai-limit-trie-logs-enabled=false +besu_container_pull: true + +prometheus_config: | + global: + scrape_interval: 12s + evaluation_interval: 30s + scrape_timeout: 10s + external_labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + ip_address: "{{ ansible_host }}" + network: "{{ ethereum_network_name }}" + testnet: "{{ ethereum_network_name }}" + execution_client: "{{ ethereum_node_el }}" + consensus_client: "{{ ethereum_node_cl }}" + supernode: "{{ ethereum_node_cl_supernode_enabled | bool | default(false) }}" + remote_write: + - queue_config: + batch_send_deadline: 5s + max_backoff: 500ms + max_samples_per_send: 500 + min_backoff: 50ms + max_shards: 100 + url: {{ prometheus_remote_push_url }} + remote_timeout: 10s + basic_auth: + username: {{ prometheus_remote_write_username }} + password: {{ prometheus_remote_write_password }} + scrape_configs: + - job_name: "prometheus" + metrics_path: "/metrics" + static_configs: + - targets: ["localhost:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "node" + metrics_path: "/metrics" + static_configs: + - targets: ["172.17.0.1:9100"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "exporter" + metrics_path: "/metrics" + static_configs: + - targets: ["ethereum-metrics-exporter:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "consensus_node" + metrics_path: "/metrics" + static_configs: + - targets: ["{{ vars[ethereum_node_cl + '_container_name'] }}:{{ ethereum_node_cl_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "execution" + metrics_path: "/metrics" + static_configs: + - targets: ["execution:{{ ethereum_node_el_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" diff --git a/ansible/inventories/devnet-7/group_vars/blobber.yaml b/ansible/inventories/devnet-7/group_vars/blobber.yaml new file mode 100644 index 0000000..60f831a --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/blobber.yaml @@ -0,0 +1,56 @@ +--- +blobber_container_image: "{{ default_tooling_images.blobber }}" + +blobber_log_level: trace + +ethereum_node_blobber_enabled: true + +# Storage configs +blobber_datadir: /data/blobber +blobber_validator_local_key_folder: "{{ ansible_inventory_sources[0] | dirname }}/files/validator_keys/{{ inventory_hostname }}" +blobber_validator_remote_key_folder: "{{ blobber_datadir }}/validators" + +blobber_container_volumes: + - "{{ blobber_datadir }}:/data" + +# Blobber specific configs +blobber_beacon_api: beacon:5052 +# blobber_slot_actions: {"name": "equivocating_blob_sidecars", "broadcast_blobs_first": true} +# blobber_slot_actions: {"name": "conflicting_blobs"} +blobber_slot_actions: { name: blob_gossip_delay, delay_milliseconds: 1500 } +blobber_slot_action_frequency: 1 + +blobber_container_ports: + - "{{ blobber_p2p_port }}:{{ blobber_p2p_port }}" + - "{{ blobber_p2p_port }}:{{ blobber_p2p_port }}/udp" + - "{{ blobber_p2p_port + 1 }}:{{ blobber_p2p_port }}" + - "{{ blobber_p2p_port + 1 }}:{{ blobber_p2p_port }}/udp" + - "{{ blobber_p2p_port + 2 }}:{{ blobber_p2p_port }}" + - "{{ blobber_p2p_port + 2 }}:{{ blobber_p2p_port }}/udp" + - "{{ blobber_p2p_port + 3 }}:{{ blobber_p2p_port }}" + - "{{ blobber_p2p_port + 3 }}:{{ blobber_p2p_port }}/udp" + - "{{ blobber_p2p_port + 4 }}:{{ blobber_p2p_port }}" + - "{{ blobber_p2p_port + 4 }}:{{ blobber_p2p_port }}/udp" + - "{{ blobber_p2p_port + 5 }}:{{ blobber_p2p_port }}" + - "{{ blobber_p2p_port + 5 }}:{{ blobber_p2p_port }}/udp" + - 127.0.0.1:{{ blobber_api_listen_port }}:{{ blobber_api_listen_port }} + +# - "--cl=https://{{ secret_nginx_shared_basic_auth.name }}:{{ secret_nginx_shared_basic_auth.password }}@{{ ethereum_node_beacon_prefix }}lighthouse-geth-1.{{ network_server_subdomain }}" +# - "--cl=https://{{ secret_nginx_shared_basic_auth.name }}:{{ secret_nginx_shared_basic_auth.password }}@{{ ethereum_node_beacon_prefix }}lighthouse-besu-1.{{ network_server_subdomain }}" +# - "--cl=https://{{ secret_nginx_shared_basic_auth.name }}:{{ secret_nginx_shared_basic_auth.password }}@{{ ethereum_node_beacon_prefix }}lighthouse-erigon-1.{{ network_server_subdomain }}" +# - "--cl=https://{{ secret_nginx_shared_basic_auth.name }}:{{ secret_nginx_shared_basic_auth.password }}@{{ ethereum_node_beacon_prefix }}lighthouse-nethermind-1.{{ network_server_subdomain }}" +# - "--cl=https://{{ secret_nginx_shared_basic_auth.name }}:{{ secret_nginx_shared_basic_auth.password }}@{{ ethereum_node_beacon_prefix }}lighthouse-reth-1.{{ network_server_subdomain }}" +blobber_container_command: + - --cl={{ blobber_beacon_api }} + - --beacon-port-start={{ blobber_p2p_port }} + - --log-level={{ blobber_log_level }} + - --external-ip=0.0.0.0 + - --slot-action={{ blobber_slot_actions | to_json }} + - --slot-action-frequency={{ blobber_slot_action_frequency }} + - --validator-key-folder=/data/validators + - --validator-proxy-port-start={{ blobber_api_listen_port }} + - --enable-unsafe-mode + +blobber_container_command_extra_args: [] +# Default image pull policy +blobber_container_pull: false diff --git a/ansible/inventories/devnet-7/group_vars/bootnode.sops.yaml b/ansible/inventories/devnet-7/group_vars/bootnode.sops.yaml new file mode 100644 index 0000000..cd4ba32 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/bootnode.sops.yaml @@ -0,0 +1,158 @@ +cl_bootnode_privkey: ENC[AES256_GCM,data:ywhGk8vFMF6ishOKGWMfSn0IxJttDiSBqaHgserxIamQvRMzJPcmMvAStVfMZB7XBHGyiWLy9vvDFhhQzBiocg==,iv:iEHtpLwDBPbJ3Ny/ZZLf+jX2nWunP21VhgU5D6fiSyQ=,tag:d2jUPi/+szwu4fkG1UuYKA==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: [] + lastmodified: "2025-01-31T05:18:36Z" + mac: ENC[AES256_GCM,data:OvHF2KM4DeBqRrBC2bhcpCrUbW2mwIvt+6+nwXld/qoDhgTxT8DBXGAfFY9wE172uElnV6O2bHGfgwCVX121Jg6d1rYzvcXrXuLaaTqLCTjy3xAkE0GrjJpXDAzhghsY95NU+n3u5Rz+49SffK/sR/dqRyNsASsW64eTzdMvXng=,iv:5di/nwLQieMrIxn6IEOP1UcJjick+X1l0kLka2e/72w=,tag:7jfPRLfWT2WiwllOfvxtEA==,type:str] + pgp: + - created_at: "2025-09-16T09:19:21Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMA32GcoRiZf6pARAAjH+IB4Omi4oRDCts8pOiPZpqGLhtexR36+2Wbgf850w5 + iHfN3I1jp/x+LhMkHF7OTjsUVCf27Q+NlAy5kv/Ptgf8Ks52N1rLzway5g+ssRWB + 7BwrRdNWvrBPVrQ3tXgHJlUyY4GfpkVFTHBZ0SP4g9VL1Ym7fK7n850XzbeNGJRx + LVXKrL8BqFa6Yunc4QP8megBAZTFLktUBPwZfSycs6aZKixRDiad/cb4JWXk2wl/ + byiFcYb1GynJbcLxjRdPqbkAk9EeDObXIatudLtNKB/iHnGst0KozBOwNHXD0v5N + 9kjJ++FlMHy205cMkUQj/ZVZgUgPhg2EOUvtUNOh160PIDqoDVQAcdKahjvgSF4h + sM0FPEigOu4qUncv9ckPjiyzT/vQPH3ZwDri1OMeOtpsBIx64kPZkx7w6Z60RaNQ + E9qa8tc/h4Co2NftEY9z6au5n9QN8J+b8z46DCbCKvfMRXffcUNZc9AcqeQ0add3 + I3xzdynbuQKbLLj2MRrBatrv+x1goEddXGXDfDaKmrYQJomsppYHuf2WOcamRLaW + Fx3FkMNnbcTzsGJQHHvRglOWacMeeSedQb55WePZUPO8P4im+vkihhqOX9J7Pufq + h7LNhub+IACZ02Tqrc8x53SsWSw9BDE67PpAPfhIopUrosUdy/dKRxYVWHFlpLjS + UQGmHup2nSh0C32VGJa5RdpFW6vTsgigKLS8nxULQMJ/9hxLrfpIRXLlVMxK89wk + bE5b1RsyNJ6UNXy5vI4gDmXpyXd7H5/QRDxPzLrf4WiipA== + =tx9b + -----END PGP MESSAGE----- + fp: 80DB2B4EF6CA4D9829C280605636BC0E08138A24 + - created_at: "2025-09-16T09:19:21Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMA87Wber4r4dGAQ//T+tC6M6C2NaDxpjRdsB2/+6bj9YhUIFL4EhDgasNYybW + ewY3/zTwfcVdWaz7hNup6NaYqkGlVTGLGEeGLD8LsKN1QXSQD+jrkVO3kbJ8juZf + hABQbOplI/x5IFz63qvid1A16xuxURCclo6GjL/Z8tpLjU/QYx5Y+jI7sXXeymxC + m9sg79pYBMyKyF4WoSJ7ftrIkwE93rEGJPjTQ76kismwV/RNf3dWIiypSLC6xdXD + d9DfCzN26iYK1GCONqYRDgOnHkvpFWvxXaUw6vmNm7uUUzRn/9x1mUJixL6GoY+9 + vTp6Qw4v534QjRAwG1G4lpv2GzliyKV6QI7AvhVoup3BzQBHnIUf7P3so0Ug33qK + cw2YvaNZosEuFFjaHUl+8Q+FIYXSc/ctG61eCeraqNnzToBzsb2P6uk2UDspy0Dk + lKTsqCyvW5GIPhVgAa8zBY24IEB85QfqWOdifZioRDbvl3x+IBozcTHwv8BxCoxm + p1VejU7lTlaEKt8QiQgi/ox1SRAQaMtICaG4uGBdLqb9fQxfolBZbNPfOlSSsTCn + DepMfCG31l2tuQQVUTo821fqsmQR9/zG3pzM3qgz+C8BPzovR6zronVASPoSNLvP + Pgk1S9N5/skbQ4jSqBiVyoyddkmEgHXlRsENueaj4Mobzoi9PX0PLjDAx911k+HS + UQHcYCMo6RecBliX4xFJ+Eu7h75QVgTe0o0ZeJnIzng9qfSX9qbpz5q7l1JtsWPV + 5P3tIxwWRrOoUhM35DGJNYsshFXx/jVZLQRB9hxxkJGekg== + =P8j2 + -----END PGP MESSAGE----- + fp: 69F66EEA7AE36CCB77DDB8CA1BC39532FB4A2DBD + - created_at: "2025-09-16T09:19:21Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMA4hw3nPn82LyAQ/+KXu4QcH7jEK0FOIkpPgFAKAxgTr7iodgHnyMF2orvyT4 + Kns5IKt9REOgbhZ6fKx07lcf3HUPe3rMPJ13Xy2dG8lRc3ucul/L7q2OR7+9vOVt + EC+AIokjSzEkyDtIvWXFArP20qmEyThcMWJWY531VTdrXtv2RhyH3hBwBFWpweMr + B3N5WCVcr90retI3k0dCgbiz7CK4tlUortc8fTQJtwo3FGbO3spojFMqf4DkU4ag + PGEhsWHPY6RyoxBQCFR2yWGWuV7ZSpF3KoKQBDYlXzSpBG82N96OppbqDl59P3KN + LGKSRIsvq4UNR+dge9jtOdkealEVfo4VSjDAfOx8ceUAUn6MAGSAvyK3xqd5AFc4 + rBTMavg2rNqfrJsHuSlnVstqi+WOd+c6JnEZHwyqHPGxUHzjUOjpO/u1cmkJk4+J + KhbeZwNlNsHpA0V3olYgZHOmyCya6y9IOhB5HJrNzKYbUwzIaEXygc1/AMvzNDye + d/xUI0p3BHglra8tC3FUStsO5WJnplc41YlcteQrWaI/lsh4FQFMhpBBRTYyXMDC + JZGGcS+VejZGh55kmMfVjc9jy1S6WcOQrYFk58ZYcW4M2/6xHVnI9u0RVI8pDNLb + sdiDF+NMMmpZfx7Bu51K4tzoW2zZeoYgkY7sf3feDEsQ1Fa8m1GzAqpmnnOaknDS + UQEZG9MTHeZNdAYIHpcX72L702w0VLx+w9WJSbt/HBYpdDPdfiHe4r1SWhlWBDHo + obhPTmQyl9BP77gRvv7YaVnbd1OzHyugVY9mXcPexKV76A== + =bvyI + -----END PGP MESSAGE----- + fp: D1002590180DE371BCB455EAEFCD4ACD0B4D3F6E + - created_at: "2025-09-16T09:19:21Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAw01qfIBwY0JAQ/+JlPvzxbn8hp8QXmtxy18SsKYXxBQ4VAXpOsOgsKkqPXD + sGjeJtVGPal0u3avizQzuua+4wOD+3FLHM4Pyo0sgpIm5LG50shyerIC2qVnChBB + boxsy/qeYk7XYq7zgXAhoo9PE5rQX2jC+AZ28QOfP7K6g1yewJqLVuyS3uUt2HoI + r+EBHrict4RPd6klAhPNN+z813+kGP/ySkoqfkObFz25qorXk9YGqoQW+tfX5XCg + Ows2iX+/0m/u5SX30fTVnzbBnxabDerTQj5XPycdhWVR+YBeDIsTd1PxCk+xKrqr + CP4lXEcXkbuiOD8bCZ0Nfias9F/G4sYMYD4y+LMKe3yNcQ5VP5+JYCiBucl8OdTc + r75PxyciQZy6a8sctXQtG3HOuPNvDVi+OSIyFQ09HHYCC4y0j6yfW7YYP5Tv6vMm + yXy0lT/tc4IIEfFxHBQyNWNN6xZlHW0TBe+s0RQ2cnbAdKk9imfnhRF6+3KF0nsJ + WYXk5yShxxW3S1ujm4C+LZFv6bAZz1RN7krNFbeaMNZJkx6Yf9sVzZIOdcxik0oN + LDvnTuxXRLEBBGEgOBIbvVQHQGDZzdHmbaXlpKnnV3iEL9sjXl+qBVXIwzp+D5Nc + UM3WbuDs6LIr+O6XWqMZrhz+HSH2dKi8If72+DkCB+8HNZWNYLiWe8sqA/kEzt3S + UQGPRo12reWMKimS7gmCZSFmCkhAxzvbYdJP8ToTmxFKbCTxsItLnHoCbSD4S94W + tnwa5hhxezaxP1uvGFUfwvHPXMma9ChbFnZL1l2wuFaVUg== + =nxty + -----END PGP MESSAGE----- + fp: B9F81F327CF5346860E85269D7AF98F214C59E4E + - created_at: "2025-09-16T09:19:21Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQEMAwDFOgk9tOQ+AQf/S3r7EsW1J1wcBKkrWws4xazvLUrkOPV7Pev4quKpSHG2 + d9pr45IFFW3zgqjqBpUzCzo+a9wXM2/IzHYe44wQSHmmhzXijmFARZ0TXMgtiAg+ + EJUdwrbhglemA0s1WPlRyGIBdVvkfUWFpb9EGMqsB9gqGo/JNmTyUiG3+47e8iUf + JTp2RbmdCSvDY78QiDF7r31Tw4Rrn+ep4FTMyUw/XY7539mz4D6/6pX01Tb7tGxV + V8yzYchD9kg3R3Jwgq2UcU9f9yGDa9epmo0qDfwb+ECpv1SsS41VSkGk9/KDTjr0 + 8LuhrsUg8S4ZCHVfEuOrs+s1RBq2tJU+esKXB9/sJNJRAWqYRPTSIy4Ytq7zT0DO + dATxseHf3gAhP9eBlKyBDvFcNPDp+Lhs7akJndrRm/8n/s3GvEJJAej3ur9ETOTI + 4BYMwVrwQkwtwGmrZXT+iXh1 + =sklM + -----END PGP MESSAGE----- + fp: 0600D41E1313E31016F7C55BF597BC0C5F22D1A2 + - created_at: "2025-09-16T09:19:21Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAz4a8AV36xppARAAhikgDq91xe/hHoTK+nJjxjPe7HnabWcvaYSElOTcJdT4 + R2z8fgJJ8SRn+XSFIZZbufPChxhAga5L+KlO3d+9ifvHttDZ/wKPitMfUne1M9mX + SR+NcK1tKJn5iQJ9Bxx50l7u/ZFTnvDHfis37NDQnrU3osEQmefc+zc/YHfDtXYb + LU3HVqlSzciNpKLrJtYi1Y6l8BFgnnzxMyfUAn7gL16K6xh4wUxUOvesiQPltXRQ + bkEGvAakdOg6x9LBN8w9o2d0mV1/WADPKjPgyF2Rrgv7gU0ZpU2qracvUaIvCqfG + vg5Tj34NZAqc1Gaa7Y1tuRyijLJPT2FR0EH3Qd9SBvEeyAMjgQ1wbPuvwVioNpqg + sjXKALKL2O1RIPT2FTcbsPHUMlUyWU5J4HZ5sbTeZGDcYt9Izk7C1wJMvH1922iC + FUobJ52hAkUr05ensTqp36nbvU/7MIFiOB7uO4ro12UV90SyIPDZgjXBZYzAER8B + ggX56/zzjQyUzfA8z2NWzSj21WY45d3e+rXKU7MIlmvlqG21jyIN7nizzCQkQxIm + 8kwEAuPJHS2nDbewA2dgWF4uTCPq9BH1QWYD4az4bxtuuF56O2C/zIN6m90UmLoU + U840pAg4jq7EWiDSr9Y8Bop2E3rXnY8bKHvoj8ouLH2CE3M6ozC08qxORKCg20LS + UQGLDqG3LoDHmupHH5OBw7GdWg4mhnfn1fXW/3mvXeC6+lY43BGxHBeJV6MIXFDn + winRBpyrrOpf7JyDa6evUuhrYzckgiatZjLYPKUqXdyVuQ== + =QKuz + -----END PGP MESSAGE----- + fp: 29C50D01122FDE78E257482DAA497EB2610A8435 + - created_at: "2025-09-16T09:19:21Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hQIMAxYzhHRfYJYtAQ/5AXwnZoyGM43yGkK1zVhO9JBz3B/ZhOHtReRbQCHoET6P + Mi9KuzuDWbXLq90z0dKMY1vDTJCHOThsQiqiR4LVJCiFHuDdgCpkVBGA+aoA8QgZ + DdynxVKXbaIQRC3Cc1rDe/lFDfsMeibebsCgxX3rWytho75DvkPVgKqakCk6qZJp + Z3wD0MH7DWps1A+fB+Q+W+n6uZFxIJ8OtPVdSU4H7FZ42WkxZZ3xxygJsVYzUdT2 + 7qWqArs3K7HRTO2Tx0kssGUo0IYTvrYwWSJjDyqQCe5hATzqY7ailzttiWNtM7Tl + XCkwb+QpvAkTFVwFdI+meZEVgme//+ZM+5slTg2ACALG+diwzDwca5EQtKheMdN5 + BBrQuh08Mnz5QJA3mLtvRFpYeogaTb82EaoXvm+BlydUKrdL+kuuK9AoeF2CvYuy + or1zc2nrfeTCRWRtWlfeJxdBkNMkxnVFwnhik5jvQmCnKZdZboWZIiTO5ABeRpCz + peh7dwLIK92GPLHWYsvKjGsINuDxawpX4iOCJSgg1JlzDp/Qim5hjCaTcrfUXXOl + WNK5gUt56Ij8Uk0GEmRhECgmKEuByLEpOKYeG0nLBopkNxKWd8RUvK8GCx+nlb+s + 7KmlrzvpqqLpXFJ5u0PjiRiw0dOJhSahwK+yROE8qQh3nVsJlnWDnjvcfh1g2BnS + UQH8UO8RDIVvocBWXnNYXAoSbv2UVE10HxphyAmMmgXB4Su2M4yXajMg6TlWtkZq + tHX+uD/SdA44VImJmsdE4AwP+yODWSYgj98ziSLWAKTPBA== + =Gc8d + -----END PGP MESSAGE----- + fp: 9BE537027CB7467923E240FF2AED09371C121F91 + - created_at: "2025-09-16T09:19:21Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hF4DjS7VXZx9i8YSAQdA+QaCeWae6exqCLoTbViPC5MHCVuc5FHuJBfgKpTDzBcw + z666dWfo9PmP7os82CM0gvMSEwoKihm0z8Khux3yGGQFXgQlTavPCsmnEOBtqntH + 1FsBCQIQq8ASFMaLcLuUJQi4Jn7B4LJJt692Venh5ajhah5uOet0IHLxaTsYxpTL + VDFruyur7UAz73in3NcDKQsdsVly/1YvAqUDUNQZCR46JD1a6inxBvNSy8nn + =kExY + -----END PGP MESSAGE----- + fp: F93098C3D8ED15D0924A6DDAEB4E93A88660C55B + unencrypted_suffix: _unencrypted + version: 3.7.3 diff --git a/ansible/inventories/devnet-7/group_vars/bootnode.yaml b/ansible/inventories/devnet-7/group_vars/bootnode.yaml new file mode 100644 index 0000000..1a11d30 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/bootnode.yaml @@ -0,0 +1,173 @@ +--- +# ethereum_cl_bootnode: "{{ hostvars[primary_bootnode]['bootnodoor_fact_enr'] }}" +# ethereum_el_bootnode: "{{ hostvars[primary_bootnode]['bootnodoor_fact_enode'] }}" + +ethereum_cl_bootnodes: >- + {{ + ( + ['prysm-ethrex-1', 'prysm-geth-1', 'lodestar-ethrex-1', 'lodestar-geth-1'] + | map('extract', hostvars, ['ethereum_node_fact_discovery_cl_enr']) + | select('defined') + | list + ) + }} + +ethereum_el_bootnodes: >- + {{ + ( + ['prysm-ethrex-1', 'prysm-geth-1', 'lodestar-ethrex-1', 'lodestar-geth-1'] + | map('extract', hostvars, ['ethereum_node_fact_discovery_el_enode']) + | select('defined') + | list + ) + }} + + + +# role: eth_inventory_web +eth_inventory_web_container_networks: "{{ docker_networks_shared }}" +eth_inventory_web_container_env: + VIRTUAL_HOST: "{{ server_fqdn }}" + VIRTUAL_PORT: "80" + VIRTUAL_PATH: /meta/api + VIRTUAL_DEST: / + LETSENCRYPT_HOST: "{{ server_fqdn }}" + +# role: ethpandaops.general.bootnodoor +bootnodoor_privkey: >- + {{ + (secret_bootnodoor_seed ~ ':' ~ ethereum_genesis_chain_id|string) + | hash('sha256') + }} +bootnodoor_set_facts: true +bootnodoor_el_enabled: true +bootnodoor_cl_enabled: true + +bootnodoor_container_name: bootnodoor +bootnodoor_container_image: ethpandaops/bootnodoor:master +bootnodoor_container_networks: "{{ docker_networks_shared }}" +bootnodoor_p2p_port: 9010 +bootnodoor_ui_port: 8004 +bootnodoor_enr_ip: "{{ ansible_host }}" + +bootnodoor_el_config: /network-config/genesis.json +bootnodoor_el_genesis_hash: /network-config/deposit_contract_block_hash.txt +bootnodoor_cl_config: /network-config/config.yaml +bootnodoor_cl_gvr: /network-config/genesis_validators_root.txt +bootnodoor_container_volumes_extra: + - "{{ eth_testnet_config_dir }}:/network-config:ro" + +bootnodoor_container_command_extra_args: [] +bootnodoor_container_env: + VIRTUAL_HOST: bootnodoor-{{ server_fqdn }} + VIRTUAL_PORT: "{{ bootnodoor_ui_port | string }}" + LETSENCRYPT_HOST: bootnodoor-{{ server_fqdn }} + +# role: ethpandaops.general.ethereum_node +ethereum_node_cl: prysm +ethereum_node_cl_validator_enabled: false + +prysm_container_tty: true +prysm_pprof_enabled: true +prysm_validator_container_tty: true +# role: ethpandaops.general.prysm +prysm_container_name: beacon +prysm_validator_container_name: validator +prysm_container_image: "{{ default_ethereum_client_images.prysm }}" +prysm_container_env: + VIRTUAL_HOST: "{{ ethereum_node_beacon_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_cl_ports_http_beacon | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_beacon_hostname }}" +prysm_container_volumes: + - "{{ prysm_datadir }}:/data" + - "{{ prysm_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +prysm_container_entrypoint: + - /app/cmd/beacon-chain/beacon-chain +prysm_container_command_extra_args: >- + {{ prysm_container_command_extra_simple_args + prysm_container_command_extra_bootnode_args }} +prysm_container_command_extra_simple_args: + - --grpc-gateway-corsdomain=* + - --chain-config-file=/network-config/config.yaml + - --genesis-state=/network-config/genesis.ssz + - --contract-deployment-block={{ ethereum_network_deposit_contract_block }} + - --min-sync-peers=1 + - --verbosity=debug + - --subscribe-all-subnets +prysm_container_command_extra_bootnode_args: >- + {{ ethereum_cl_bootnodes | map('regex_replace', '^', '--bootstrap-node=') | list }} +# role: ethpandaops.general.ethereum_node +ethereum_node_el: geth + +# role: ethpandaops.general.geth +geth_container_name: execution +geth_container_image: "{{ default_ethereum_client_images.geth }}" +geth_container_env: + VIRTUAL_HOST: "{{ ethereum_node_rcp_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_el_ports_http_rpc | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_rcp_hostname }}" +geth_container_volumes: + - "{{ geth_datadir }}:/data" + - "{{ geth_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +geth_container_command_extra_args: + - --override.genesis=/network-config/genesis.json + - --http.api=eth,net,web3,debug,admin,txpool + - --http.vhosts=* + - --networkid={{ ethereum_network_id }} + - --syncmode=full +geth_container_pull: true +# role: ethpandaops.general.prometheus +prometheus_remote_push_url: https://victoriametrics.ethdevops.io/insert/0/prometheus/api/v1/write +prometheus_remote_write_username: "{{ secret_prometheus_remote_write.username }}" +prometheus_remote_write_password: "{{ secret_prometheus_remote_write.password }}" +prometheus_config: | + global: + scrape_interval: 30s + evaluation_interval: 30s + scrape_timeout: 10s + external_labels: + instance: "{{ inventory_hostname }}" + ip_address: "{{ ansible_host }}" + network: "{{ ethereum_network_name }}" + testnet: "{{ ethereum_network_name }}" + execution_client: "{{ ethereum_node_el }}" + consensus_client: "{{ ethereum_node_cl }}" + supernode: "{{ ethereum_node_cl_supernode_enabled | bool | default(false) }}" + remote_write: + - queue_config: + batch_send_deadline: 5s + max_backoff: 500ms + max_samples_per_send: 500 + min_backoff: 50ms + max_shards: 100 + url: {{ prometheus_remote_push_url }} + remote_timeout: 10s + basic_auth: + username: {{ prometheus_remote_write_username }} + password: {{ prometheus_remote_write_password }} + scrape_configs: + - job_name: "prometheus" + metrics_path: "/metrics" + static_configs: + - targets: ["localhost:9090"] + labels: + instance: "{{ inventory_hostname }}" + - job_name: "node" + metrics_path: "/metrics" + static_configs: + - targets: ["172.17.0.1:9100"] + labels: + instance: "{{ inventory_hostname }}" + - job_name: "exporter" + metrics_path: "/metrics" + static_configs: + - targets: ["ethereum-metrics-exporter:9090"] + labels: + instance: "{{ inventory_hostname }}" + - job_name: "consensus_node" + metrics_path: "/metrics" + static_configs: + - targets: ["{{ vars[ethereum_node_cl + '_container_name'] }}:{{ ethereum_node_cl_ports_metrics }}"] + labels: + instance: "{{ inventory_hostname }}" diff --git a/ansible/inventories/devnet-7/group_vars/buildoor.yaml b/ansible/inventories/devnet-7/group_vars/buildoor.yaml new file mode 100644 index 0000000..843cf92 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/buildoor.yaml @@ -0,0 +1,63 @@ +--- +# NOTE: precedence over sibling groups (prysm/ethrex) is established via +# `[buildoor:vars] ansible_group_priority=100` in inventory.ini — that variable +# is consulted while loading group_vars, so it cannot be set here. + +buildoor_container_image: "{{ default_tooling_images.buildoor }}" +buildoor_container_pull: true +buildoor_container_networks: "{{ docker_networks_shared }}" + +buildoor_jwt_path: /data/execution-auth.secret +buildoor_cl_client: http://beacon:{{ ethereum_node_cl_ports_http_beacon }} +buildoor_el_engine_api: http://execution:{{ ethereum_node_el_ports_engine }} +buildoor_el_rpc: http://execution:{{ ethereum_node_el_ports_http_rpc }} + +# Avoid conflict with prysm metrics on 127.0.0.1:8080 +buildoor_api_port: 8085 + +# Verify API tokens against the centralized authenticatoor service for this devnet. +buildoor_auth_provider_url: https://auth.{{ network_subdomain }} +buildoor_overview_url: https://buildoor.{{ network_subdomain }} + +# Wallet ECDSA private key +buildoor_wallet_privkey: "{{ secret_buildoor_wallet_privkey }}" +# Builder BLS private key +buildoor_builder_mnemonic: "{{ secret_buildoor_builder_mnemonic }}" +buildoor_builder_mnemonic_index: "{{ builder_index }}" + +buildoor_container_command_extra_args: + - --validator-ranges-url=https://config.{{ network_subdomain }}/api/v1/nodes/validator-ranges +buildoor_container_env: + VIRTUAL_HOST: api-{{ server_fqdn }} + VIRTUAL_PORT: "{{ buildoor_api_port | string }}" + LETSENCRYPT_HOST: api-{{ server_fqdn }} + BUILDOOR_INJECT_HEAD_HTML: + +# Re-apply the prysm.yaml merge so we don't lose the bootnode args while adding --prepare-all-payloads +prysm_container_command_extra_args: >- + {{ prysm_container_command_extra_simple_args + + prysm_container_command_extra_bootnode_args + + ['--prepare-all-payloads'] }} +lighthouse_container_command_extra_args: >- + {{ lighthouse_container_command_extra_simple_args + + ['--always-prepare-payload'] }} +lodestar_container_command_extra_args: >- + {{ lodestar_container_command_extra_simple_args + + ['--emitPayloadAttributes'] }} + +teku_container_command_extra_args: >- + {{ teku_container_command_extra_simple_args + + ['--Xfork-choice-updated-always-send-payload-attributes=true'] }} + +grandine_container_command_extra_args: >- + {{ grandine_container_command_extra_simple_args + + ['--features=AlwaysPrepareExecutionPayload'] }} + + +ethereum_node_docker_watchtower_containers_list: + - execution + - beacon + - validator + - xatu-sentry + - buildoor diff --git a/ansible/inventories/devnet-7/group_vars/dns_server.yaml b/ansible/inventories/devnet-7/group_vars/dns_server.yaml new file mode 100644 index 0000000..3155e2e --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/dns_server.yaml @@ -0,0 +1,93 @@ +--- +# role: ethpandaops.general.dns_server +dns_server_disable_systemd_resolved: true +dns_server_is_master: "{{ inventory_hostname == primary_bootnode }}" +dns_server_acme_zone: "{{ network_server_subdomain }}" + +# One or more master IPs (for slaves to pull from / accept NOTIFY from) +dns_server_master: "{{ ([hostvars[primary_bootnode].ansible_host] if (groups.get('bootnode') is defined and primary_bootnode in groups['bootnode']) else []) | list + }}" + +# All slave IPs (for master's also-notify / allow-transfer) +dns_server_slave: "{{ (groups.get('bootnode', []) | difference([primary_bootnode])) | map('extract', hostvars, 'ansible_host') | list }}" + +dns_server_zones: + - zone: "{{ network_server_subdomain }}" + content: | + $TTL 120 + @ IN SOA {{ server_fqdn }}. zonemaster.{{ domain }}. ( + 00000000000000 ; Serial + 2H ; Refresh + 1H ; Retry + 1W ; Expire + 5m ) ; NX (TTL Negativ Cache) + IN NS {{ server_fqdn }}. + {# extra NS for each secondary, if their FQDNs are known #} + {% for h in groups['bootnode'] | sort if h != primary_bootnode %} + {% if hostvars[h].server_fqdn is defined %} + IN NS {{ hostvars[h].server_fqdn }}. + {% endif %} + {% endfor %} + + ; certificates + cert IN A {{ hostvars[primary_bootnode]['ansible_host'] }} + + ; bootnodes + {% for host in groups['bootnode'] | sort %} + {{ hostvars[host]['inventory_hostname'] }} IN A {{ hostvars[host]['ansible_host'] }} + {{ ethereum_node_rpc_prefix }}{{ hostvars[host]['inventory_hostname'] }} IN A {{ hostvars[host]['ansible_host'] }} + {{ ethereum_node_beacon_prefix }}{{ hostvars[host]['inventory_hostname'] }} IN A {{ hostvars[host]['ansible_host'] }} + bootnodoor-{{ hostvars[host]['inventory_hostname'] }} IN A {{ hostvars[host]['ansible_host'] }} + {% if hostvars[host]['ipv6'] is defined %} + {{ hostvars[host]['inventory_hostname'] }} IN AAAA {{ hostvars[host]['ipv6'] }} + {% set proxy_ipv6 = hostvars[host].get('docker_nginx_proxy_public_ipv6', hostvars[host]['ipv6']) %} + {{ ethereum_node_rpc_prefix }}{{ hostvars[host]['inventory_hostname'] }} IN AAAA {{ proxy_ipv6 if proxy_ipv6 | length > 0 else hostvars[host]['ipv6'] }} + {{ ethereum_node_beacon_prefix }}{{ hostvars[host]['inventory_hostname'] }} IN AAAA {{ proxy_ipv6 if proxy_ipv6 | length > 0 else hostvars[host]['ipv6'] }} + {% endif %} + {% endfor %} + + ; ethereum_nodes + {% for host in groups['ethereum_node'] | sort %} + {{ hostvars[host]['inventory_hostname'] }} IN A {{ hostvars[host]['ansible_host'] }} + {{ ethereum_node_rpc_prefix }}{{ hostvars[host]['inventory_hostname'] }} IN A {{ hostvars[host]['ansible_host'] }} + {{ ethereum_node_beacon_prefix }}{{ hostvars[host]['inventory_hostname'] }} IN A {{ hostvars[host]['ansible_host'] }} + {% if hostvars[host]['ipv6'] is defined %} + {{ hostvars[host]['inventory_hostname'] }} IN AAAA {{ hostvars[host]['ipv6'] }} + {% set proxy_ipv6 = hostvars[host].get('docker_nginx_proxy_public_ipv6', hostvars[host]['ipv6']) %} + {{ ethereum_node_rpc_prefix }}{{ hostvars[host]['inventory_hostname'] }} IN AAAA {{ proxy_ipv6 if proxy_ipv6 | length > 0 else hostvars[host]['ipv6'] }} + {{ ethereum_node_beacon_prefix }}{{ hostvars[host]['inventory_hostname'] }} IN AAAA {{ proxy_ipv6 if proxy_ipv6 | length > 0 else hostvars[host]['ipv6'] }} + {% endif %} + {% endfor %} + + ; mev-relay + {% for host in groups.get('mev_relay', []) | sort %} + {{ hostvars[host]['inventory_hostname'] }} IN A {{ hostvars[host]['ansible_host'] }} + {% if hostvars[host]['ipv6'] is defined %} + {{ hostvars[host]['inventory_hostname'] }} IN AAAA {{ hostvars[host]['ipv6'] }} + {% endif %} + {% endfor %} + + ; buildoor + {% for host in groups.get('buildoor', []) | sort %} + api-{{ hostvars[host]['inventory_hostname'] }} IN A {{ hostvars[host]['ansible_host'] }} + {% if hostvars[host]['ipv6'] is defined %} + {% set proxy_ipv6 = hostvars[host].get('docker_nginx_proxy_public_ipv6', hostvars[host]['ipv6']) %} + api-{{ hostvars[host]['inventory_hostname'] }} IN AAAA {{ proxy_ipv6 if proxy_ipv6 | length > 0 else hostvars[host]['ipv6'] }} + {% endif %} + {% endfor %} + +# role: ethpandaops.general.wildcard_cert_issuer +wildcard_cert_issuer_enabled: "{{ inventory_hostname == primary_bootnode }}" +wildcard_cert_issuer_base_domain: "{{ network_server_subdomain }}" +wildcard_cert_issuer_acme_directory: https://acme.zerossl.com/v2/DV90 +wildcard_cert_issuer_acme_eab_kid: "{{ secret_zerossl.ACME_EAB_KID }}" +wildcard_cert_issuer_acme_eab_hmac_key: "{{ secret_zerossl.ACME_EAB_HMAC_KEY }}" +wildcard_cert_issuer_email: ssl@ethpandaops.io +wildcard_cert_issuer_publish_port: 8080 +wildcard_cert_issuer_publish_psk: "{{ secret_cert_encryption_psk }}" +wildcard_cert_issuer_container_env: + VIRTUAL_HOST: cert.{{ network_server_subdomain }} + VIRTUAL_PORT: "8080" + HTTPS_METHOD: nohttps +wildcard_cert_issuer_container_networks: "{{ docker_networks_shared }}" +wildcard_cert_issuer_rfc2136_server: 172.17.0.1 # dns container runs on host network diff --git a/ansible/inventories/devnet-7/group_vars/erigon.yaml b/ansible/inventories/devnet-7/group_vars/erigon.yaml new file mode 100644 index 0000000..c176248 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/erigon.yaml @@ -0,0 +1,83 @@ +--- +# role: ethpandaops.general.bootstrap +erigon_extra_github_users: [] # handles not yet in the coredevs registry +bootstrap_default_user_authorized_keys_github_team_el: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/erigon?format=txt') | select | list) + + erigon_extra_github_users }} + +# role: ethpandaops.general.ethereum_node +ethereum_node_el: erigon + +# role: ethpandaops.general.erigon +erigon_container_name: execution +erigon_container_image: "{{ default_ethereum_client_images.erigon }}" +erigon_container_env: + VIRTUAL_HOST: "{{ ethereum_node_rcp_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_el_ports_http_rpc | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_rcp_hostname }}" +erigon_container_command_extra_args: + - --http.api=eth,erigon,engine,web3,net,debug,trace,txpool,admin + - --http.vhosts=* + - --ws + - --networkid={{ ethereum_network_id }} + - --db.size.limit=500GB + - --bootnodes={{ ethereum_el_bootnodes | join(',') }} +erigon_init_custom_network: true +erigon_init_custom_network_genesis_file: "{{ eth_testnet_config_dir }}/genesis.json" + +prometheus_config: | + global: + scrape_interval: 30s + evaluation_interval: 30s + scrape_timeout: 10s + external_labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + ip_address: "{{ ansible_host }}" + network: "{{ ethereum_network_name }}" + testnet: "{{ ethereum_network_name }}" + execution_client: "{{ ethereum_node_el }}" + consensus_client: "{{ ethereum_node_cl }}" + supernode: "{{ ethereum_node_cl_supernode_enabled | bool | default(false) }}" + remote_write: + - queue_config: + batch_send_deadline: 5s + max_backoff: 500ms + max_samples_per_send: 500 + min_backoff: 50ms + max_shards: 100 + url: {{ prometheus_remote_push_url }} + remote_timeout: 10s + basic_auth: + username: {{ prometheus_remote_write_username }} + password: {{ prometheus_remote_write_password }} + scrape_configs: + - job_name: "prometheus" + metrics_path: "/metrics" + static_configs: + - targets: ["localhost:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "node" + metrics_path: "/metrics" + static_configs: + - targets: ["172.17.0.1:9100"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "exporter" + metrics_path: "/metrics" + static_configs: + - targets: ["ethereum-metrics-exporter:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "consensus_node" + metrics_path: "/metrics" + static_configs: + - targets: ["{{ vars[ethereum_node_cl + '_container_name'] }}:{{ ethereum_node_cl_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "execution" + metrics_path: "/debug/metrics/prometheus" + static_configs: + - targets: ["execution:{{ ethereum_node_el_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" diff --git a/ansible/inventories/devnet-7/group_vars/ethereum_node.yaml b/ansible/inventories/devnet-7/group_vars/ethereum_node.yaml new file mode 100644 index 0000000..0381ed6 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/ethereum_node.yaml @@ -0,0 +1,114 @@ +--- +ethereum_cl_bootnodes: >- + {{ + ( + [primary_bootnode] + | map('extract', hostvars, ['bootnodoor_fact_enr']) + | select('defined') + | list + ) + + + ( + [primary_bootnode, 'prysm-ethrex-1', 'prysm-geth-1', 'lodestar-ethrex-1', 'lodestar-geth-1', 'nimbus-ethrex-1', 'nimbus-geth-1', 'lighthouse-ethrex-1', 'lighthouse-geth-1'] + | map('extract', hostvars, ['ethereum_node_fact_discovery_cl_enr']) + | select('defined') + | list + ) + }} + +ethereum_el_bootnodes: >- + {{ + ( + [primary_bootnode] + | map('extract', hostvars, ['bootnodoor_fact_enode']) + | select('defined') + | list + ) + + + ( + [primary_bootnode, 'prysm-ethrex-1', 'prysm-geth-1', 'lodestar-ethrex-1', 'lodestar-geth-1', 'nimbus-ethrex-1', 'nimbus-geth-1', 'lighthouse-ethrex-1', 'lighthouse-geth-1'] + | map('extract', hostvars, ['ethereum_node_fact_discovery_el_enode']) + | select('defined') + | list + ) + }} + + +ethereum_node_xatu_sentry_enabled: true + +# role: ethpandaops.general.brakebear +brakebear_download_speed: "{{ ethereum_node_cl_supernode_enabled | ternary('1000Mbps', '100Mbps') }}" +brakebear_upload_speed: "{{ ethereum_node_cl_supernode_enabled | ternary('1000Mbps', '50Mbps') }}" +brakebear_config: | + log_level: "info" + docker_containers: + - name: "execution" + download_rate: {{ brakebear_download_speed }} + upload_rate: {{ brakebear_upload_speed }} + exclusions: + private-networks: true + docker-networks: + names: ["*"] + - name: "beacon" + download_rate: {{ brakebear_download_speed }} + upload_rate: {{ brakebear_upload_speed }} + exclusions: + private-networks: true + docker-networks: + names: ["*"] + + +# role: ethpandaops.general.prometheus +prometheus_remote_push_url: https://victoriametrics-public.analytics.production.platform.ethpandaops.io/insert/1/prometheus +prometheus_remote_write_username: "{{ secret_prometheus_remote_write.username }}" +prometheus_remote_write_password: "{{ secret_prometheus_remote_write.password }}" +prometheus_config: | + global: + scrape_interval: 30s + evaluation_interval: 30s + scrape_timeout: 10s + external_labels: + instance: "{{ inventory_hostname }}" + ip_address: "{{ ansible_host }}" + network: "{{ ethereum_network_name }}" + testnet: "{{ ethereum_network_name }}" + execution_client: "{{ ethereum_node_el }}" + consensus_client: "{{ ethereum_node_cl }}" + supernode: "{{ ethereum_node_cl_supernode_enabled | bool | default(false) }}" + remote_write: + - queue_config: + batch_send_deadline: 5s + max_backoff: 500ms + max_samples_per_send: 500 + min_backoff: 50ms + max_shards: 100 + url: {{ prometheus_remote_push_url }} + remote_timeout: 10s + basic_auth: + username: {{ prometheus_remote_write_username }} + password: {{ prometheus_remote_write_password }} + scrape_configs: + - job_name: "prometheus" + metrics_path: "/metrics" + static_configs: + - targets: ["localhost:9090"] + labels: + instance: "{{ inventory_hostname }}" + - job_name: "node" + metrics_path: "/metrics" + static_configs: + - targets: ["172.17.0.1:9100"] + labels: + instance: "{{ inventory_hostname }}" + - job_name: "exporter" + metrics_path: "/metrics" + static_configs: + - targets: ["ethereum-metrics-exporter:9090"] + labels: + instance: "{{ inventory_hostname }}" + - job_name: "consensus_node" + metrics_path: "/metrics" + static_configs: + - targets: ["{{ vars[ethereum_node_cl + '_container_name'] }}:{{ ethereum_node_cl_ports_metrics }}"] + labels: + instance: "{{ inventory_hostname }}" diff --git a/ansible/inventories/devnet-7/group_vars/ethrex.yaml b/ansible/inventories/devnet-7/group_vars/ethrex.yaml new file mode 100644 index 0000000..8af2be0 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/ethrex.yaml @@ -0,0 +1,86 @@ +--- +# role: ethpandaops.general.bootstrap +ethrex_extra_github_users: [] # handles not yet in the coredevs registry +bootstrap_default_user_authorized_keys_github_team_el: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/ethrex?format=txt') | select | list) + + ethrex_extra_github_users }} + +ethereum_node_fact_discovery_el_key_cmd: + ethrex: xxd -p -c32 {{ ethrex_datadir | default('/data/ethrex') }}/chain-{{ ethereum_network_id | trim }}/node.key + +# role: ethpandaops.general.ethereum_node +ethereum_node_el: ethrex +# role: ethpandaops.general.ethrex +ethrex_container_name: execution +ethrex_container_image: "{{ default_ethereum_client_images.ethrex }}" +ethrex_container_env: + VIRTUAL_HOST: "{{ ethereum_node_rcp_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_el_ports_http_rpc | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_rcp_hostname }}" + RUST_LOG: info +ethrex_container_volumes: + - "{{ ethrex_datadir }}:/data" + - "{{ ethrex_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +ethrex_container_command_extra_args: + - --network=/network-config/genesis.json + - --bootnodes={{ ethereum_el_bootnodes | join(',') }} + - --http.api=eth,net,web3,admin,debug + - --syncmode=full + +prometheus_config: | + global: + scrape_interval: 30s + evaluation_interval: 30s + scrape_timeout: 10s + external_labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + ip_address: "{{ ansible_host }}" + network: "{{ ethereum_network_name }}" + testnet: "{{ ethereum_network_name }}" + execution_client: "{{ ethereum_node_el }}" + consensus_client: "{{ ethereum_node_cl }}" + supernode: "{{ ethereum_node_cl_supernode_enabled | bool | default(false) }}" + remote_write: + - queue_config: + batch_send_deadline: 5s + max_backoff: 500ms + max_samples_per_send: 500 + min_backoff: 50ms + max_shards: 100 + url: {{ prometheus_remote_push_url }} + remote_timeout: 10s + basic_auth: + username: {{ prometheus_remote_write_username }} + password: {{ prometheus_remote_write_password }} + scrape_configs: + - job_name: "prometheus" + metrics_path: "/metrics" + static_configs: + - targets: ["localhost:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "node" + metrics_path: "/metrics" + static_configs: + - targets: ["172.17.0.1:9100"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "exporter" + metrics_path: "/metrics" + static_configs: + - targets: ["ethereum-metrics-exporter:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "consensus_node" + metrics_path: "/metrics" + static_configs: + - targets: ["{{ vars[ethereum_node_cl + '_container_name'] }}:{{ ethereum_node_cl_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "execution" + metrics_path: "/metrics" + static_configs: + - targets: ["execution:{{ ethereum_node_el_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" diff --git a/ansible/inventories/devnet-7/group_vars/geth.yaml b/ansible/inventories/devnet-7/group_vars/geth.yaml new file mode 100644 index 0000000..4f76420 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/geth.yaml @@ -0,0 +1,86 @@ +--- +# role: ethpandaops.general.bootstrap +geth_extra_github_users: [] # handles not yet in the coredevs registry +bootstrap_default_user_authorized_keys_github_team_el: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/geth?format=txt') | select | list) + + geth_extra_github_users }} + +# role: ethpandaops.general.ethereum_node +ethereum_node_el: geth + +# role: ethpandaops.general.geth +geth_container_name: execution +geth_container_image: "{{ default_ethereum_client_images.geth }}" +geth_container_env: + VIRTUAL_HOST: "{{ ethereum_node_rcp_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_el_ports_http_rpc | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_rcp_hostname }}" +geth_container_volumes: + - "{{ geth_datadir }}:/data" + - "{{ geth_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +geth_container_command_extra_args: + - --override.genesis=/network-config/genesis.json + - --http.api=eth,net,web3,debug,admin,txpool + - --http.vhosts=* + - --networkid={{ ethereum_network_id }} + - --syncmode=full + - --bootnodes={{ ethereum_el_bootnodes | join(',') }} +geth_container_pull: true + +prometheus_config: | + global: + scrape_interval: 30s + evaluation_interval: 30s + scrape_timeout: 10s + external_labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + ip_address: "{{ ansible_host }}" + network: "{{ ethereum_network_name }}" + testnet: "{{ ethereum_network_name }}" + execution_client: "{{ ethereum_node_el }}" + consensus_client: "{{ ethereum_node_cl }}" + supernode: "{{ ethereum_node_cl_supernode_enabled | bool | default(false) }}" + remote_write: + - queue_config: + batch_send_deadline: 5s + max_backoff: 500ms + max_samples_per_send: 500 + min_backoff: 50ms + max_shards: 100 + url: {{ prometheus_remote_push_url }} + remote_timeout: 10s + basic_auth: + username: {{ prometheus_remote_write_username }} + password: {{ prometheus_remote_write_password }} + scrape_configs: + - job_name: "prometheus" + metrics_path: "/metrics" + static_configs: + - targets: ["localhost:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "node" + metrics_path: "/metrics" + static_configs: + - targets: ["172.17.0.1:9100"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "exporter" + metrics_path: "/metrics" + static_configs: + - targets: ["ethereum-metrics-exporter:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "consensus_node" + metrics_path: "/metrics" + static_configs: + - targets: ["{{ vars[ethereum_node_cl + '_container_name'] }}:{{ ethereum_node_cl_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "execution" + metrics_path: "/debug/metrics/prometheus" + static_configs: + - targets: ["execution:{{ ethereum_node_el_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" diff --git a/ansible/inventories/devnet-7/group_vars/grandine.yaml b/ansible/inventories/devnet-7/group_vars/grandine.yaml new file mode 100644 index 0000000..7c06e2a --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/grandine.yaml @@ -0,0 +1,54 @@ +--- +# role: ethpandaops.general.bootstrap +grandine_extra_github_users: [] # handles not yet in the coredevs registry +bootstrap_default_user_authorized_keys_github_team_cl: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/grandine?format=txt') | select | list) + + grandine_extra_github_users }} + +# role: geerlingguy.docker +docker_daemon_options: + log-driver: json-file + log-opts: + max-size: 500m + max-file: "40" + +# role: validator_keys +validator_keys_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/teku-keys/" + dest: "{{ grandine_validator_datadir }}/keys/" + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/teku-secrets/" + dest: "{{ grandine_validator_datadir }}/secrets/" + +validator_bls_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/change_operations.json" + dest: "{{ grandine_validator_datadir }}/change_operations.json" + owner: grandine + group: grandine +# role: ethpandaops.general.ethereum_node +ethereum_node_cl: grandine + +# role: ethpandaops.general.grandine +grandine_container_name: beacon +grandine_container_image: "{{ default_ethereum_client_images.grandine }}" +grandine_container_env: + VIRTUAL_HOST: "{{ ethereum_node_beacon_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_cl_ports_http_beacon | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_beacon_hostname }}" + GRANDINE_LOG: fork_choice_control=debug,p2p=debug +grandine_container_volumes: + - "{{ grandine_datadir }}:/data" + - "{{ grandine_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +grandine_container_command_extra_args: >- + {{ grandine_container_command_extra_simple_args }} +grandine_container_command_extra_simple_args: + - --configuration-directory=/network-config/ + - --boot-nodes={{ ethereum_cl_bootnodes | join(',') }} + - --graffiti={{ ansible_hostname }} + - --features=LogHttpRequests + - --default-gas-limit=190000000 + +grandine_validator_container_volumes: + - "{{ grandine_validator_datadir }}:/validator-data" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +grandine_validator_datadir: /data/grandine-validator diff --git a/ansible/inventories/devnet-7/group_vars/lighthouse.yaml b/ansible/inventories/devnet-7/group_vars/lighthouse.yaml new file mode 100644 index 0000000..16557a9 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/lighthouse.yaml @@ -0,0 +1,53 @@ +--- +# role: ethpandaops.general.bootstrap +lighthouse_extra_github_users: [] # handles not yet in the coredevs registry +bootstrap_default_user_authorized_keys_github_team_cl: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/lighthouse?format=txt') | select | list) + + lighthouse_extra_github_users }} + +# role: validator_keys +validator_keys_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/keys/" + dest: "{{ lighthouse_validator_datadir }}/keys/" + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/secrets/" + dest: "{{ lighthouse_validator_datadir }}/secrets/" + +validator_bls_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/change_operations.json" + dest: "{{ lighthouse_validator_datadir }}/change_operations.json" + owner: lighthouse + group: lighthouse + +# role: ethpandaops.general.ethereum_node +ethereum_node_cl: lighthouse + +# role: ethpandaops.general.lighthouse +lighthouse_container_name: beacon +lighthouse_validator_container_name: validator +lighthouse_container_image: "{{ default_ethereum_client_images.lighthouse }}" +lighthouse_container_env: + VIRTUAL_HOST: "{{ ethereum_node_beacon_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_cl_ports_http_beacon | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_beacon_hostname }}" +lighthouse_container_volumes: + - "{{ lighthouse_datadir }}:/data" + - "{{ lighthouse_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +lighthouse_container_command_extra_args: >- + {{ lighthouse_container_command_extra_simple_args }} +lighthouse_container_command_extra_simple_args: + - --testnet-dir=/network-config + - --boot-nodes={{ ethereum_cl_bootnodes | join(',') }} + - --suggested-fee-recipient={{ lighthouse_validator_fee_recipient }} + - --ignore-ws-check + - --enable-mplex +lighthouse_validator_container_volumes: + - "{{ lighthouse_validator_datadir }}:/validator-data" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +lighthouse_validator_container_command_extra_args: + - --testnet-dir=/network-config + - --graffiti={{ ansible_hostname }} + - --gas-limit=190000000 + - --builder-proposals +lighthouse_validator_datadir: /data/lighthouse-validator +lighthouse_container_pull: true diff --git a/ansible/inventories/devnet-7/group_vars/lodestar.yaml b/ansible/inventories/devnet-7/group_vars/lodestar.yaml new file mode 100644 index 0000000..2b55c64 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/lodestar.yaml @@ -0,0 +1,54 @@ +--- +# role: ethpandaops.general.bootstrap +lodestar_extra_github_users: # handles not yet in the coredevs registry + - lodekeeper +bootstrap_default_user_authorized_keys_github_team_cl: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/lodestar?format=txt') | select | list) + + lodestar_extra_github_users }} + +# role: validator_keys +validator_keys_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/keys/" + dest: "{{ lodestar_validator_datadir }}/keys/" + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/secrets/" + dest: "{{ lodestar_validator_datadir }}/secrets/" + +validator_bls_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/change_operations.json" + dest: "{{ lodestar_validator_datadir }}/change_operations.json" + owner: lodestar + group: lodestar + +# role: ethpandaops.general.ethereum_node +ethereum_node_cl: lodestar + +# role: ethpandaops.general.lodestar +lodestar_container_name: beacon +lodestar_validator_container_name: validator +lodestar_container_image: "{{ default_ethereum_client_images.lodestar }}" +lodestar_container_env: + VIRTUAL_HOST: "{{ ethereum_node_beacon_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_cl_ports_http_beacon | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_beacon_hostname }}" +lodestar_container_volumes: + - "{{ lodestar_datadir }}:/data" + - "{{ lodestar_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +lodestar_container_command_extra_args: >- + {{ lodestar_container_command_extra_simple_args }} +lodestar_container_command_extra_simple_args: + - --paramsFile=/network-config/config.yaml + - --genesisStateFile=/network-config/genesis.ssz + - --bootnodes={{ ethereum_cl_bootnodes | join(',') }} + - --rest.namespace="*" + - --network.connectToDiscv5Bootnodes + - --nat=true +lodestar_validator_container_volumes: + - "{{ lodestar_validator_datadir }}:/validator-data" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +lodestar_validator_container_command_extra_args: + - --paramsFile=/network-config/config.yaml + - --graffiti={{ ansible_hostname }} + - --defaultGasLimit=190000000 +lodestar_validator_datadir: /data/lodestar-validator +lodestar_container_pull: true diff --git a/ansible/inventories/devnet-7/group_vars/nethermind.yaml b/ansible/inventories/devnet-7/group_vars/nethermind.yaml new file mode 100644 index 0000000..61aba9d --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/nethermind.yaml @@ -0,0 +1,92 @@ +--- +# role: ethpandaops.general.bootstrap +nethermind_extra_github_users: [] # handles not yet in the coredevs registry +bootstrap_default_user_authorized_keys_github_team_el: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/nethermind?format=txt') | select | list) + + nethermind_extra_github_users }} + +# role: ethpandaops.general.ethereum_node +ethereum_node_el: nethermind +# role: ethpandaops.general.nethermind +nethermind_container_name: execution +nethermind_container_image: "{{ default_ethereum_client_images.nethermind }}" +nethermind_container_env: + VIRTUAL_HOST: "{{ ethereum_node_rcp_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_el_ports_http_rpc | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_rcp_hostname }}" +nethermind_container_entrypoint: + - /nethermind/nethermind +nethermind_container_volumes: + - "{{ nethermind_datadir }}:/data" + - "{{ nethermind_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +nethermind_container_command_extra_args: + - --Init.ChainSpecPath=/network-config/chainspec.json + - --JsonRpc.EnabledModules=Eth,Subscribe,Trace,TxPool,Web3,Personal,Proof,Net,Parity,Health,Rpc,Debug,Admin + - --Discovery.Bootnodes={{ ethereum_el_bootnodes | join(',') }} + - --Discovery.DiscoveryVersion=All + - --Pruning.Mode=None + - --config=none + - --log=INFO + - --Seq.MinLevel=Info + - --Seq.ServerUrl={{ nethermind_seq_server }} + - --Seq.ApiKey={{ nethermind_seq_api_key }} + - --Metrics.PushGatewayUrl={{ nethermind_push_gateway }} +nethermind_container_pull: true + +prometheus_config: | + global: + scrape_interval: 30s + evaluation_interval: 30s + scrape_timeout: 10s + external_labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + ip_address: "{{ ansible_host }}" + network: "{{ ethereum_network_name }}" + testnet: "{{ ethereum_network_name }}" + execution_client: "{{ ethereum_node_el }}" + consensus_client: "{{ ethereum_node_cl }}" + supernode: "{{ ethereum_node_cl_supernode_enabled | bool | default(false) }}" + remote_write: + - queue_config: + batch_send_deadline: 5s + max_backoff: 500ms + max_samples_per_send: 500 + min_backoff: 50ms + max_shards: 100 + url: {{ prometheus_remote_push_url }} + remote_timeout: 10s + basic_auth: + username: {{ prometheus_remote_write_username }} + password: {{ prometheus_remote_write_password }} + scrape_configs: + - job_name: "prometheus" + metrics_path: "/metrics" + static_configs: + - targets: ["localhost:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "node" + metrics_path: "/metrics" + static_configs: + - targets: ["172.17.0.1:9100"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "exporter" + metrics_path: "/metrics" + static_configs: + - targets: ["ethereum-metrics-exporter:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "consensus_node" + metrics_path: "/metrics" + static_configs: + - targets: ["{{ vars[ethereum_node_cl + '_container_name'] }}:{{ ethereum_node_cl_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "execution" + metrics_path: "/metrics" + static_configs: + - targets: ["execution:{{ ethereum_node_el_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" diff --git a/ansible/inventories/devnet-7/group_vars/nimbus.yaml b/ansible/inventories/devnet-7/group_vars/nimbus.yaml new file mode 100644 index 0000000..663e38f --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/nimbus.yaml @@ -0,0 +1,58 @@ +--- +# role: ethpandaops.general.bootstrap +nimbus_extra_github_users: [] # handles not yet in the coredevs registry +bootstrap_default_user_authorized_keys_github_team_cl: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/nimbus?format=txt') | select | list) + + nimbus_extra_github_users }} + +# role: validator_keys +validator_keys_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/nimbus-keys/" + dest: "{{ nimbus_validator_datadir }}/keys/" + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/secrets/" + dest: "{{ nimbus_validator_datadir }}/secrets/" + +validator_bls_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/change_operations.json" + dest: "{{ nimbus_validator_datadir }}/change_operations.json" + owner: nimbus + group: nimbus + +# role: ethpandaops.general.ethereum_node +ethereum_node_cl: nimbus +ethereum_node_cl_checkpoint_sync_enabled: false +# Checkpointsync +nimbus_checkpoint_autoremove_enabled: true +nimbus_checkpoint_container_command_extra_args: + - --trusted-node-url={{ checkpoint_sync_url }} + - --network=/network-config + - --backfill=false +# role: ethpandaops.general.nimbus +nimbus_container_name: beacon +nimbus_container_image: "{{ default_ethereum_client_images.nimbus }}" +nimbus_container_env: + VIRTUAL_HOST: "{{ ethereum_node_beacon_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_cl_ports_http_beacon | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_beacon_hostname }}" +nimbus_container_volumes: + - "{{ nimbus_datadir }}:/data" + - "{{ nimbus_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +nimbus_container_command_extra_args: >- + {{ nimbus_container_command_extra_simple_args + nimbus_container_command_extra_bootnode_args }} +nimbus_container_command_extra_simple_args: + - --network=/network-config + - --validator-monitor-auto=false + - --doppelganger-detection=off + - --log-level=DEBUG + - --light-client-data-serve=true + - --graffiti={{ ansible_hostname }} + - --suggested-gas-limit=190000000 + +nimbus_container_command_extra_bootnode_args: >- + {{ ethereum_cl_bootnodes | map('regex_replace', '^', '--bootstrap-node=') | list }} + +nimbus_validator_datadir: /data/nimbus-validator +nimbus_container_validator_volumes: + - "{{ nimbus_validator_datadir }}:/validator-data" +nimbus_container_pull: true diff --git a/ansible/inventories/devnet-7/group_vars/nimbusel.yaml b/ansible/inventories/devnet-7/group_vars/nimbusel.yaml new file mode 100644 index 0000000..aa608e8 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/nimbusel.yaml @@ -0,0 +1,83 @@ +--- +# role: ethpandaops.general.bootstrap +nimbusel_extra_github_users: [] # handles not yet in the coredevs registry +bootstrap_default_user_authorized_keys_github_team_el: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/nimbus?format=txt') | select | list) + + nimbusel_extra_github_users }} + +# role: ethpandaops.general.ethereum_node +ethereum_node_el: nimbusel +# role: ethpandaops.general.nimbusel +nimbusel_container_name: execution +nimbusel_container_image: "{{ default_ethereum_client_images.nimbusel }}" +nimbusel_container_env: + VIRTUAL_HOST: "{{ ethereum_node_rcp_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_el_ports_http_rpc | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_rcp_hostname }}" + +nimbusel_container_volumes: + - "{{ nimbusel_datadir }}:/data" + - "{{ nimbusel_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +nimbusel_container_command_extra_args: + - --network=/network-config/genesis.json + - --bootstrap-node={{ ethereum_el_bootnodes | join(',') }} + - --rpc-api=admin,eth,debug + - --ws-api=admin,eth,debug + +prometheus_config: | + global: + scrape_interval: 30s + evaluation_interval: 30s + scrape_timeout: 10s + external_labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + ip_address: "{{ ansible_host }}" + network: "{{ ethereum_network_name }}" + testnet: "{{ ethereum_network_name }}" + execution_client: "{{ ethereum_node_el }}" + consensus_client: "{{ ethereum_node_cl }}" + supernode: "{{ ethereum_node_cl_supernode_enabled | bool | default(false) }}" + remote_write: + - queue_config: + batch_send_deadline: 5s + max_backoff: 500ms + max_samples_per_send: 500 + min_backoff: 50ms + max_shards: 100 + url: {{ prometheus_remote_push_url }} + remote_timeout: 10s + basic_auth: + username: {{ prometheus_remote_write_username }} + password: {{ prometheus_remote_write_password }} + scrape_configs: + - job_name: "prometheus" + metrics_path: "/metrics" + static_configs: + - targets: ["localhost:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "node" + metrics_path: "/metrics" + static_configs: + - targets: ["172.17.0.1:9100"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "exporter" + metrics_path: "/metrics" + static_configs: + - targets: ["ethereum-metrics-exporter:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "consensus_node" + metrics_path: "/metrics" + static_configs: + - targets: ["{{ vars[ethereum_node_cl + '_container_name'] }}:{{ ethereum_node_cl_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "execution" + metrics_path: "/metrics" + static_configs: + - targets: ["execution:{{ ethereum_node_el_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" diff --git a/ansible/inventories/devnet-7/group_vars/prysm.yaml b/ansible/inventories/devnet-7/group_vars/prysm.yaml new file mode 100644 index 0000000..8009dd8 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/prysm.yaml @@ -0,0 +1,68 @@ +--- +# role: ethpandaops.general.bootstrap +prysm_extra_github_users: # handles not yet in the coredevs registry + - syjn99 +bootstrap_default_user_authorized_keys_github_team_cl: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/prysm?format=txt') | select | list) + + prysm_extra_github_users }} + +# role: validator_keys +validator_keys_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/prysm_wallet_pass.txt" + dest: "{{ prysm_validator_datadir }}/wallet_pass.txt" + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/prysm/direct/accounts/all-accounts.keystore.json" + dest: "{{ prysm_validator_datadir }}/wallet/direct/accounts/all-accounts.keystore.json" + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/prysm/keymanageropts.json" + dest: "{{ prysm_validator_datadir }}/wallet/direct/keymanageropts.json" + +validator_bls_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/change_operations.json" + dest: "{{ prysm_validator_datadir }}/change_operations.json" + owner: prysm + group: prysm + +# role: ethpandaops.general.ethereum_node +ethereum_node_cl: prysm +prysm_container_tty: true +prysm_pprof_enabled: true +prysm_validator_container_tty: true +# role: ethpandaops.general.prysm +prysm_container_name: beacon +prysm_validator_container_name: validator +prysm_container_image: "{{ default_ethereum_client_images.prysm }}" +prysm_container_env: + VIRTUAL_HOST: "{{ ethereum_node_beacon_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_cl_ports_http_beacon | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_beacon_hostname }}" +prysm_container_volumes: + - "{{ prysm_datadir }}:/data" + - "{{ prysm_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +prysm_container_entrypoint: + - /app/cmd/beacon-chain/beacon-chain +prysm_container_command_extra_args: >- + {{ prysm_container_command_extra_simple_args + prysm_container_command_extra_bootnode_args }} +prysm_container_command_extra_simple_args: + - --grpc-gateway-corsdomain=* + - --chain-config-file=/network-config/config.yaml + - --genesis-state=/network-config/genesis.ssz + - --contract-deployment-block={{ ethereum_network_deposit_contract_block }} + - --min-sync-peers=1 + - --verbosity=debug + - --subscribe-all-subnets + - --p2p-colocation-whitelist=0.0.0.0/0,::/0 +prysm_container_command_extra_bootnode_args: >- + {{ ethereum_cl_bootnodes | map('regex_replace', '^', '--bootstrap-node=') | list }} + +prysm_validator_container_image: "{{ default_ethereum_client_images.prysm_validator }}" +prysm_validator_container_volumes: + - "{{ prysm_validator_datadir }}:/validator-data" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +prysm_validator_container_entrypoint: + - /app/cmd/validator/validator +prysm_validator_container_command_extra_args: + - --chain-config-file=/network-config/config.yaml + - --graffiti={{ ansible_hostname }} + - --suggested-gas-limit=190000000 + - --enable-builder +prysm_validator_datadir: /data/prysm-validator diff --git a/ansible/inventories/devnet-7/group_vars/reth.yaml b/ansible/inventories/devnet-7/group_vars/reth.yaml new file mode 100644 index 0000000..5910d11 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/reth.yaml @@ -0,0 +1,82 @@ +--- +# role: ethpandaops.general.bootstrap +reth_extra_github_users: [] # handles not yet in the coredevs registry +bootstrap_default_user_authorized_keys_github_team_el: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/reth?format=txt') | select | list) + + reth_extra_github_users }} + +# role: ethpandaops.general.ethereum_node +ethereum_node_el: reth +# role: ethpandaops.general.reth +reth_container_name: execution +reth_container_image: "{{ default_ethereum_client_images.reth }}" +reth_container_env: + VIRTUAL_HOST: "{{ ethereum_node_rcp_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_el_ports_http_rpc | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_rcp_hostname }}" + RUST_BACKTRACE: full +reth_container_volumes: + - "{{ reth_datadir }}:/data" + - "{{ reth_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" +reth_container_command_extra_args: + - --chain=/network-config/genesis.json + - --bootnodes={{ ethereum_el_bootnodes | join(',') }} + - --http.api=trace,rpc,eth,net,debug,web3,admin,txpool + - --builder.gaslimit=150000000 +prometheus_config: | + global: + scrape_interval: 30s + evaluation_interval: 30s + scrape_timeout: 10s + external_labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + ip_address: "{{ ansible_host }}" + network: "{{ ethereum_network_name }}" + testnet: "{{ ethereum_network_name }}" + execution_client: "{{ ethereum_node_el }}" + consensus_client: "{{ ethereum_node_cl }}" + supernode: "{{ ethereum_node_cl_supernode_enabled | bool | default(false) }}" + remote_write: + - queue_config: + batch_send_deadline: 5s + max_backoff: 500ms + max_samples_per_send: 500 + min_backoff: 50ms + max_shards: 100 + url: {{ prometheus_remote_push_url }} + remote_timeout: 10s + basic_auth: + username: {{ prometheus_remote_write_username }} + password: {{ prometheus_remote_write_password }} + scrape_configs: + - job_name: "prometheus" + metrics_path: "/metrics" + static_configs: + - targets: ["localhost:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "node" + metrics_path: "/metrics" + static_configs: + - targets: ["172.17.0.1:9100"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "exporter" + metrics_path: "/metrics" + static_configs: + - targets: ["ethereum-metrics-exporter:9090"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "consensus_node" + metrics_path: "/metrics" + static_configs: + - targets: ["{{ vars[ethereum_node_cl + '_container_name'] }}:{{ ethereum_node_cl_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" + - job_name: "execution" + metrics_path: "/metrics" + static_configs: + - targets: ["execution:{{ ethereum_node_el_ports_metrics }}"] + labels: + instance: "{{ ethereum_network_name }}-{{ inventory_hostname }}" diff --git a/ansible/inventories/devnet-7/group_vars/teku.yaml b/ansible/inventories/devnet-7/group_vars/teku.yaml new file mode 100644 index 0000000..d611e69 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/teku.yaml @@ -0,0 +1,56 @@ +--- +# role: ethpandaops.general.bootstrap +teku_extra_github_users: # handles not yet in the coredevs registry + - siladu +bootstrap_default_user_authorized_keys_github_team_cl: >- + {{ (query('ethpandaops.general.url_cached', coredevs_api_url ~ '/api/v1/users/teku?format=txt') | select | list) + + teku_extra_github_users }} + +# role: validator_keys +validator_keys_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/teku-keys/" + dest: "{{ teku_validator_datadir }}/keys/" + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/teku-secrets/" + dest: "{{ teku_validator_datadir }}/secrets/" + +validator_bls_sync_files: + - src: "{{ inventory_dir }}/files/validator_keys/{{ inventory_hostname }}/change_operations.json" + dest: "{{ teku_validator_datadir }}/change_operations.json" + owner: teku + group: teku +# role: ethpandaops.general.ethereum_node +ethereum_node_cl: teku + +# role: ethpandaops.general.teku +teku_container_name: beacon +teku_container_image: "{{ default_ethereum_client_images.teku }}" +teku_container_env: + VIRTUAL_HOST: "{{ ethereum_node_beacon_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_cl_ports_http_beacon | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_beacon_hostname }}" +teku_container_volumes: + - "{{ teku_datadir }}:/data" + - "{{ teku_auth_jwt_path }}:/execution-auth.jwt:ro" + - "{{ eth_testnet_config_dir }}:/network-config:ro" + +teku_container_command_extra_args : >- + {{ teku_container_command_extra_simple_args}} + + +teku_container_command_extra_simple_args: + - --network=/network-config/config.yaml + - --genesis-state=/network-config/genesis.ssz + - --p2p-discovery-bootnodes={{ ethereum_cl_bootnodes | join(',') }} + - --p2p-peer-upper-bound=100 + - --data-storage-non-canonical-blocks-enabled=true + - --data-storage-mode=PRUNE + - --validators-graffiti={{ ansible_hostname }} + - --logging=info + - --Xlog-include-p2p-warnings-enabled + - --metrics-block-timing-tracking-enabled + - --ignore-weak-subjectivity-period-enabled + - --rest-api-docs-enabled + - --validators-builder-registration-default-gas-limit=190000000 +teku_validator_container_volumes: + - "{{ teku_validator_datadir }}:/validator-data" +teku_validator_datadir: /data/teku-validator diff --git a/ansible/inventories/devnet-7/group_vars/tx_fuzz_blobs.yaml b/ansible/inventories/devnet-7/group_vars/tx_fuzz_blobs.yaml new file mode 100644 index 0000000..c9d8ee7 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/tx_fuzz_blobs.yaml @@ -0,0 +1,13 @@ +--- +tx_fuzz_blobs_container_image: "{{ default_tooling_images.tx_fuzz }}" +tx_fuzz_blobs_container_name: tx_fuzz_blobs + +tx_fuzz_blobs_container_command: + - blobs + - --no-al + - --rpc=https://{{ secret_nginx_shared_basic_auth.name }}:{{ secret_nginx_shared_basic_auth.password }}@{{ ethereum_node_rpc_prefix }}{{ primary_bootnode + }}.{{ network_server_subdomain }} + - --sk={{ tx_fuzz_blobs_privkey }} + - --txcount=10 + +tx_fuzz_container_networks: "{{ docker_networks_shared }}" diff --git a/ansible/inventories/devnet-7/group_vars/tx_fuzz_txs.yaml b/ansible/inventories/devnet-7/group_vars/tx_fuzz_txs.yaml new file mode 100644 index 0000000..226680f --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/tx_fuzz_txs.yaml @@ -0,0 +1,12 @@ +--- +tx_fuzz_container_image: "{{ default_tooling_images.tx_fuzz }}" +tx_fuzz_container_name: tx_fuzz_txs + +tx_fuzz_container_command: + - spam + - --no-al + - --rpc=https://{{ secret_nginx_shared_basic_auth.name }}:{{ secret_nginx_shared_basic_auth.password }}@{{ ethereum_node_rpc_prefix }}{{ primary_bootnode + }}.{{ network_server_subdomain }} + - --sk={{ tx_fuzz_txs_privkey }} + +tx_fuzz_container_networks: "{{ docker_networks_shared }}" diff --git a/ansible/inventories/devnet-7/group_vars/xatu.yaml b/ansible/inventories/devnet-7/group_vars/xatu.yaml new file mode 100644 index 0000000..e498e88 --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/xatu.yaml @@ -0,0 +1,88 @@ +--- +# role: ethpandaops.general.xatu_sentry +xatu_sentry_config_name: "{{ ethereum_network_name }}-{{ inventory_hostname }}" +xatu_sentry_config_server_address: "{{ secret_xatu_sentry.server_address }}" +xatu_sentry_config_server_auth_user: "{{ secret_xatu_sentry.user }}" +xatu_sentry_config_server_auth_password: "{{ secret_xatu_sentry.password }}" +xatu_sentry_config_network_name_override: "{{ ethereum_network_name }}" +xatu_sentry_container_networks: "{{ docker_networks_shared }}" +xatu_sentry_container_image: "{{ default_tooling_images.xatu_sentry }}" + +ethereum_node_xatu_sentry_enabled: true + +xatu_sentry_container_command: + - sentry + - --config=/config.yaml + +xatu_sentry_config: + logging: info + metricsAddr: :9090 + name: "{{ xatu_sentry_config_name }}" + ntpServer: time.google.com + ethereum: + beaconNodeAddress: http://{{ vars[ethereum_node_cl + '_container_name'] }}:{{ethereum_node_cl_ports_http_beacon}} + overrideNetworkName: "{{ xatu_sentry_config_network_name_override }}" + beaconSubscriptions: + - attestation + - block + - block_gossip + - chain_reorg + - finalized_checkpoint + - head + - voluntary_exit + - contribution_and_proof + - blob_sidecar + attestationData: + enabled: false + beaconCommittees: + enabled: false + forkChoice: + enabled: false + outputs: + - config: + address: "{{ xatu_sentry_config_server_address }}" + headers: + authorization: Basic {{ (xatu_sentry_config_server_auth_user + ':' + xatu_sentry_config_server_auth_password) | b64encode }} + maxExportBatchSize: 32 + maxQueueSize: 1000000 + workers: 5 + tls: true + filter: + eventNames: + - BEACON_API_ETH_V1_EVENTS_UNKNOWN + - BEACON_API_ETH_V1_BEACON_COMMITTEE + - BEACON_API_ETH_V1_EVENTS_BLOB_SIDECAR + - BEACON_API_ETH_V1_EVENTS_BLOCK + - BEACON_API_ETH_V1_EVENTS_BLOCK_V2 + - BEACON_API_ETH_V1_EVENTS_BLOCK_GOSSIP + - BEACON_API_ETH_V1_EVENTS_CHAIN_REORG + - BEACON_API_ETH_V1_EVENTS_CHAIN_REORG_V2 + - BEACON_API_ETH_V1_EVENTS_FINALIZED_CHECKPOINT + - BEACON_API_ETH_V1_EVENTS_FINALIZED_CHECKPOINT_V2 + - BEACON_API_ETH_V1_EVENTS_HEAD + - BEACON_API_ETH_V1_EVENTS_HEAD_V2 + - BEACON_API_ETH_V1_EVENTS_VOLUNTARY_EXIT + - BEACON_API_ETH_V1_EVENTS_VOLUNTARY_EXIT_V2 + - BEACON_API_ETH_V1_EVENTS_CONTRIBUTION_AND_PROOF + - BEACON_API_ETH_V1_EVENTS_CONTRIBUTION_AND_PROOF_V2 + - BEACON_API_ETH_V1_VALIDATOR_ATTESTATION_DATA + - MEMPOOL_TRANSACTION + - MEMPOOL_TRANSACTION_V2 + - BEACON_API_ETH_V2_BEACON_BLOCK + - BEACON_API_ETH_V2_BEACON_BLOCK_V2 + name: grpc-general + type: xatu + - config: + address: "{{ xatu_sentry_config_server_address }}" + headers: + authorization: Basic {{ (xatu_sentry_config_server_auth_user + ':' + xatu_sentry_config_server_auth_password) | b64encode }} + maxExportBatchSize: 128 + maxQueueSize: 1000000 + workers: 10 + tls: true + filter: + eventNames: + - BEACON_API_ETH_V1_EVENTS_ATTESTATION + - BEACON_API_ETH_V1_EVENTS_ATTESTATION_V2 + name: grpc-attestation + type: xatu diff --git a/ansible/inventories/devnet-7/group_vars/xatu_sentry.yaml b/ansible/inventories/devnet-7/group_vars/xatu_sentry.yaml new file mode 100644 index 0000000..f49b32b --- /dev/null +++ b/ansible/inventories/devnet-7/group_vars/xatu_sentry.yaml @@ -0,0 +1,2 @@ +--- +ethereum_node_xatu_sentry_enabled: true diff --git a/ansible/inventories/devnet-7/host_vars/localhost b/ansible/inventories/devnet-7/host_vars/localhost new file mode 100644 index 0000000..e187c8f --- /dev/null +++ b/ansible/inventories/devnet-7/host_vars/localhost @@ -0,0 +1,2 @@ +ansible_connection: local +ansible_python_interpreter: "{{ ansible_playbook_python }}" diff --git a/scripts/deploy-eip8282.zsh b/scripts/deploy-eip8282.zsh new file mode 100755 index 0000000..1f25d13 --- /dev/null +++ b/scripts/deploy-eip8282.zsh @@ -0,0 +1,152 @@ +#!/usr/bin/env zsh +# Throwaway: deploy the EIP-8282 builder deposit/exit contracts on a live +# devnet BEFORE the gloas fork. devnet-7 genesis omits them on purpose +# (DEPLOY_EIP8282_CONTRACTS=false) so this manual workflow gets exercised. +# +# Deployment goes through the deterministic CREATE2 factory +# (0x4e59b4...956C, predeployed in devnet genesis as a well-known contract) +# with the vanity-mined salts from ethereum/execution-specs#3091 — the same +# salt+initcode pairs the EL clients' `glamsterdam-devnet-7` branches expect. +# CREATE2(factory, salt, initcode) == the addresses below; the initcode's +# runtime is byte-identical to the egg v6.1.3 genesis predeploy, and the exit +# contract's constructor sets the slot-0 excess inhibitor. +# +# Usage: ./deploy-eip8282.zsh [check] +# check only report on-chain state, do not send anything +# FORCE=1 skip the pre-gloas epoch guard + +set -u + +node="${NODE:-bootnode-1}" +network="${NETWORK:-devnet-7}" +domain="ethpandaops.io" +srv="srv" +prefix="glamsterdam" + +sops_name=$(sops --decrypt ../ansible/inventories/$network/group_vars/all/all.sops.yaml | yq -r '.secret_nginx_shared_basic_auth.name') +sops_password=$(sops --decrypt ../ansible/inventories/$network/group_vars/all/all.sops.yaml | yq -r '.secret_nginx_shared_basic_auth.password') +sops_mnemonic=$(sops --decrypt ../ansible/inventories/$network/group_vars/all/all.sops.yaml | yq -r '.secret_genesis_mnemonic') +rpc_endpoint="${RPC_ENDPOINT:-https://$sops_name:$sops_password@rpc-$node.$srv.$prefix-$network.$domain}" +bn_endpoint="${BEACON_ENDPOINT:-https://$sops_name:$sops_password@bn-$node.$srv.$prefix-$network.$domain}" + +factory="0x4e59b44847b379578588920cA78FbF26c0B4956C" + +# ethereum/execution-specs#3091: builder_deposit_factory_deploy.json / +# builder_exit_factory_deploy.json. Runtime = initcode minus the constructor +# prefix (10 bytes for deposit, 45 for exit — the exit ctor is +# PUSH32 <2^256-1> PUSH0 SSTORE followed by the codecopy-return). +typeset -A name address salt initcode ctor_hexlen +contracts=(deposit exit) + +name[deposit]="EIP-8282 builder deposit (request type 0x03)" +address[deposit]="0x0000bFF46984e3725691FA540a8C7589300D8282" +salt[deposit]="1f4f2c41c28e816e259b621c58b94b37309c8dec42c8f6e400001a46c9d96bf7" +ctor_hexlen[deposit]=20 +initcode[deposit]="61027480600a5f395ff33373fffffffffffffffffffffffffffffffffffffffe1461011c575f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146102705760015460088111605257506058565b60089003015b601190600182026001905f5b5f821115607f57810190830284830290049160010191906064565b90939004925050503660b814609f57366102705734610270575f5260205ff35b8034106102705760383567ffffffffffffffff1680633b9aca001161027057633b9aca00029034031061027057600154600101600155600354806006026004015f358155600101602035815560010160403581556001016060358155600101608035815560010160a035905560b85f5f3760b85fa0600101600355005b60035460025480820380604011610131575060405b5f5b8181146101d7578281016006026004018160b8028154815260200181600101548152602001816002015480825260401c67ffffffffffffffff16816010018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c816001015353602001816003015481526020018160040154815260200190600501549052600101610133565b91018092146101e957906002556101f4565b90505f6002555f6003555b36610242575f54600154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461023057600882820111610238575b50505f610264565b0160089003610264565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b5f555f60015560b8025ff35b5f5ffd" + +name[exit]="EIP-8282 builder exit (request type 0x04)" +address[exit]="0x000064D678505ad48F8cCb093BC65613800E8282" +salt[exit]="89abb1878437213f971f849327423ed1d9c5cdb03970cd8f0000318b3ff10119" +ctor_hexlen[exit]=90 +initcode[exit]="7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5f556101ca80602d5f395ff33373fffffffffffffffffffffffffffffffffffffffe1460e1575f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101c65760015460028111605157506057565b60029003015b601190600182026001905f5b5f821115607e57810190830284830290049160010191906063565b909390049250505036603014609e57366101c657346101c6575f5260205ff35b34106101c657600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260305f60143760445fa0600101600355005b6003546002548082038060101160f5575060105b5f5b81811461012d5782810160030260040181604402815460601b8152601401816001015481526020019060020154905260010160f7565b910180921461013f579060025561014a565b90505f6002555f6003555b36610198575f54600154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101865760028282011161018e575b50505f6101ba565b01600290036101ba565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b5f555f6001556044025ff35b5f5ffd" + +inhibitor="0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + +code_at() { cast code "$1" --rpc-url "$rpc_endpoint" 2>/dev/null } + +# Sanity: the CREATE2 factory must exist (devnet genesis predeploys it). +if [[ "$(code_at $factory)" == "0x" ]]; then + echo "CREATE2 factory $factory has no code on this network — cannot deploy." >&2 + exit 1 +fi + +# Sanity: each salt+initcode must still map to its expected vanity address. +for c in "${contracts[@]}"; do + computed=$(cast create2 --deployer "$factory" --salt "0x${salt[$c]}" --init-code "0x${initcode[$c]}" 2>/dev/null | grep -oE '0x[0-9a-fA-F]{40}' | head -1) + if [[ "${computed:l}" != "${address[$c]:l}" ]]; then + echo "CREATE2 mismatch for $c: computed $computed, expected ${address[$c]} — refusing to deploy." >&2 + exit 1 + fi +done + +report() { + local c code slot0 + for c in "${contracts[@]}"; do + code=$(code_at "${address[$c]}") + slot0=$(cast storage "${address[$c]}" 0 --rpc-url "$rpc_endpoint" 2>/dev/null) + if [[ "$code" == "0x${initcode[$c]:${ctor_hexlen[$c]}}" ]]; then + printf " \033[32m✓\033[0m %s at %s (runtime matches, slot0=%s)\n" "$c" "${address[$c]}" "${slot0:0:10}..." + elif [[ "$code" == "0x" ]]; then + printf " \033[31m✗\033[0m %s at %s — no code\n" "$c" "${address[$c]}" + else + printf " \033[31m✗\033[0m %s at %s — UNEXPECTED code (%d bytes)\n" "$c" "${address[$c]}" $(( (${#code} - 2) / 2 )) + fi + done +} + +if [[ "${1:-}" == "check" ]]; then + echo "State on $prefix-$network:" + report + exit 0 +fi + +# Guard: the deployments must land before the gloas fork activates. +gloas_epoch=$(curl -s "$bn_endpoint/eth/v1/config/spec" | jq -r '.data.GLOAS_FORK_EPOCH') +head_slot=$(curl -s "$bn_endpoint/eth/v1/beacon/headers/head" | jq -r '.data.header.message.slot') +if [[ -z "$gloas_epoch" || "$gloas_epoch" == "null" || -z "$head_slot" || "$head_slot" == "null" ]]; then + echo "Could not read GLOAS_FORK_EPOCH/head slot from $node's beacon API." >&2 + [[ "${FORCE:-0}" == "1" ]] || exit 1 +else + head_epoch=$((head_slot / 32)) + echo "Head epoch: $head_epoch | gloas fork epoch: $gloas_epoch" + if (( head_epoch >= gloas_epoch )) && [[ "${FORCE:-0}" != "1" ]]; then + echo "Gloas is already active — deploying now is too late (every block since activation is invalid without these contracts). FORCE=1 to override." >&2 + exit 1 + fi +fi + +deployer_key=$(ethereal hd keys --path="m/44'/60'/0'/0/7" --seed="$sops_mnemonic" | awk '/Private key/{print $NF}') +deployer_addr=$(ethereal hd keys --path="m/44'/60'/0'/0/7" --seed="$sops_mnemonic" | awk '/Ethereum address/{print $NF}') + +echo "Deploying EIP-8282 contracts on $prefix-$network via CREATE2 factory $factory" +echo " from: $deployer_addr (mnemonic account 7 / manual-deposits)" +for c in "${contracts[@]}"; do + echo " ${name[$c]} -> ${address[$c]}" +done +echo "Continue? (y/n)" +read -r response +[[ "$response" == "y" ]] || { echo "Aborted."; exit 0 } + +for c in "${contracts[@]}"; do + if [[ "$(code_at ${address[$c]})" != "0x" ]]; then + echo "$c already has code at ${address[$c]} — skipping." + continue + fi + echo "Deploying $c..." + # The factory takes raw calldata of salt (32 bytes) ++ initcode. + out=$(cast send "$factory" "0x${salt[$c]}${initcode[$c]}" \ + --private-key "$deployer_key" \ + --rpc-url "$rpc_endpoint" \ + --gas-limit 1500000 2>&1) + tx_status=$(echo "$out" | awk '/^status/{print $2}' | head -1) + txhash=$(echo "$out" | awk '/^transactionHash/{print $2}' | head -1) + if [[ "$tx_status" != "1" && "$out" != *'"status":"0x1"'* ]]; then + echo "$c deployment tx failed (tx=${txhash:-none}):" >&2 + echo "$out" | grep -iE 'error|revert' | head -3 >&2 + exit 1 + fi + echo " tx: $txhash" +done + +echo "" +echo "Post-deploy verification:" +report + +# The exit contract must hold the excess inhibitor until the first gloas +# system call clears it; the deposit contract intentionally has no inhibitor. +exit_slot0=$(cast storage "${address[exit]}" 0 --rpc-url "$rpc_endpoint" 2>/dev/null) +if [[ "${exit_slot0:l}" != "$inhibitor" ]]; then + echo "WARNING: exit contract slot0 is $exit_slot0, expected the excess inhibitor $inhibitor" >&2 + exit 1 +fi +echo "Exit contract inhibitor armed. Done — contracts are live ahead of gloas." diff --git a/terraform/devnet-7/ansible_inventory.tmpl b/terraform/devnet-7/ansible_inventory.tmpl new file mode 100644 index 0000000..7a2829d --- /dev/null +++ b/terraform/devnet-7/ansible_inventory.tmpl @@ -0,0 +1,122 @@ +localhost + +[all:vars] +ethereum_network_name=${ethereum_network_name} + +%{ for gid, group in groups ~} +[${replace(gid, "-", "_")}] +%{ for key, host in hosts ~} +%{ if host.group == gid ~} +${host.hostname} ansible_host=${host.ip} ipv6=${host.ipv6} cloud=${host.cloud} cloud_region=${host.region} arch=${host.arch} ethereum_node_cl_supernode_enabled=${title(host.supernode)} %{ if tonumber(host.validator_end) > 0 }validator_start=${host.validator_start} validator_end=${host.validator_end}%{ endif }%{ if host.builder_index != "" } builder_index=${host.builder_index}%{ endif } +%{ endif ~} +%{ endfor ~} +%{ if gid == "lighthouse-reth" ~} +%{ for key, host in hosts ~} +%{ if host.group == "mev-relay" ~} +${host.hostname} +%{ endif ~} +%{ endfor ~} +%{ endif ~} + +%{ endfor ~} + +%{ if !contains(keys(groups), "lighthouse-reth") ~} +[lighthouse_reth] +%{ for key, host in hosts ~} +%{ if host.group == "mev-relay" ~} +${host.hostname} +%{ endif ~} +%{ endfor ~} +%{ endif ~} + +# Consensus client groups + +%{ for cl in ["lighthouse", "lodestar", "nimbus", "teku", "prysm", "grandine"] ~} +[${cl}:children] +%{ for gid, group in groups ~} +%{ if split("-", gid)[0] == "${cl}" ~} +${replace(gid, "-", "_")} +%{ endif ~} +%{ if split("-", gid)[0] == "buildoor" && length(split("-", gid)) >= 2 ~} +%{ if split("-", gid)[1] == "${cl}" ~} +${replace(gid, "-", "_")} +%{ endif ~} +%{ endif ~} +%{ endfor ~} +%{ if cl == "lighthouse" && contains(keys(groups), "mev-relay") ~} +mev_relay +%{ endif ~} +%{ endfor ~} + +# Execution client groups + +%{ for el in ["besu", "ethereumjs", "geth", "nethermind", "erigon", "reth", "nimbusel", "ethrex"] ~} +[${el}:children] +%{ for gid, group in groups ~} +%{ if split("-", gid)[0] != "bootnode" && split("-", gid)[0] != "mev" && split("-", gid)[0] != "buildoor" ~} +%{ if length(split("-", gid)) >= 2 && split("-", gid)[1] == "${el}" ~} +${replace(gid, "-", "_")} +%{ endif ~} +%{ endif ~} +%{ if split("-", gid)[0] == "buildoor" && length(split("-", gid)) >= 3 ~} +%{ if split("-", gid)[2] == "${el}" ~} +${replace(gid, "-", "_")} +%{ endif ~} +%{ endif ~} +%{ endfor ~} +%{ if el == "reth" && contains(keys(groups), "mev-relay") ~} +mev_relay +%{ endif ~} +%{ endfor ~} + +# Buildoor host aggregator + +[buildoor:children] +%{ for gid, group in groups ~} +%{ if split("-", gid)[0] == "buildoor" ~} +${replace(gid, "-", "_")} +%{ endif ~} +%{ endfor ~} + +# ansible_group_priority MUST live in the inventory (not group_vars/) — it is consulted +# while loading group_vars, so a value placed in group_vars/buildoor.yaml is read too late +# and the buildoor overrides lose to alphabetically-later sibling groups (geth, prysm). +[buildoor:vars] +ansible_group_priority=100 + +# Global groups + +[consensus_node:children] +%{ for x,y in merge( { for gid, group in groups : split("-", gid)[0] => true... if split("-", gid)[0] != "bootnode" && split("-", gid)[0] != "mev" && split("-", gid)[0] != "buildoor" } ) ~} +${x} +%{ endfor ~} +%{ if length([for gid, group in groups : gid if split("-", gid)[0] == "buildoor"]) > 0 ~} +buildoor +%{ endif ~} + +[execution_node:children] +%{ for x,y in merge( { for gid, group in groups : split("-", gid)[1] => true... if split("-", gid)[0] != "bootnode" && split("-", gid)[0] != "mev" && split("-", gid)[0] != "buildoor" && length(split("-", gid)) >= 2 } ) ~} +${x} +%{ endfor ~} +%{ if length([for gid, group in groups : gid if split("-", gid)[0] == "buildoor"]) > 0 ~} +buildoor +%{ endif ~} + +[ethereum_node:children] +consensus_node +execution_node + +%{ if contains(keys(groups), "bootnode") ~} +[dns_server:children] +bootnode +%{ endif ~} + +[mev_boost:children] +consensus_node + +[arm] +%{ for key, host in hosts ~} +%{ if can(regex("arm", key)) ~} +${host.hostname} +%{ endif ~} +%{ endfor ~} diff --git a/terraform/devnet-7/cloudflare.tf b/terraform/devnet-7/cloudflare.tf new file mode 100644 index 0000000..67bd810 --- /dev/null +++ b/terraform/devnet-7/cloudflare.tf @@ -0,0 +1,60 @@ + +//////////////////////////////////////////////////////////////////////////////////////// +// DNS NAMES +//////////////////////////////////////////////////////////////////////////////////////// + +data "cloudflare_zone" "default" { + name = "ethpandaops.io" +} + +locals { + # Combine bootnodes from both providers + bootnodes = merge( + { + for vm in local.digitalocean_vms : vm.id => { + name = vm.name + has_ipv6 = vm.ipv6 + ipv4 = digitalocean_droplet.main[vm.id].ipv4_address + ipv6 = try(digitalocean_droplet.main[vm.id].ipv6_address, null) + } if can(regex("bootnode", vm.name)) + }, + { + for vm in local.hcloud_vms : vm.id => { + name = vm.name + has_ipv6 = vm.ipv6_enabled + ipv4 = hcloud_server.main[vm.id].ipv4_address + ipv6 = try(hcloud_server.main[vm.id].ipv6_address, null) + } if can(regex("bootnode", vm.name)) + } + ) +} + +resource "cloudflare_record" "server_record_v4" { + for_each = local.bootnodes + zone_id = data.cloudflare_zone.default.id + name = "${each.value.name}.${var.ethereum_network}" + type = "A" + value = each.value.ipv4 + proxied = false + ttl = 120 +} + +resource "cloudflare_record" "server_record_v6" { + for_each = { for k, v in local.bootnodes : k => v if v.has_ipv6 } + zone_id = data.cloudflare_zone.default.id + name = "${each.value.name}.${var.ethereum_network}" + type = "AAAA" + value = each.value.ipv6 + proxied = false + ttl = 120 +} + +resource "cloudflare_record" "server_record_ns" { + for_each = local.bootnodes + zone_id = data.cloudflare_zone.default.id + name = "srv.${var.ethereum_network}" + type = "NS" + value = "${each.value.name}.${var.ethereum_network}.${data.cloudflare_zone.default.name}" + proxied = false + ttl = 120 +} diff --git a/terraform/devnet-7/digitalocean.tf b/terraform/devnet-7/digitalocean.tf new file mode 100644 index 0000000..d0a26e9 --- /dev/null +++ b/terraform/devnet-7/digitalocean.tf @@ -0,0 +1,171 @@ +//////////////////////////////////////////////////////////////////////////////////////// +// VARIABLES +//////////////////////////////////////////////////////////////////////////////////////// +variable "digitalocean_project_name" { + type = string + default = "glamsterdam-devnets" +} + +variable "digitalocean_ssh_key_name" { + type = string + default = "shared-devops-eth2" +} + +variable "digitalocean_supernode_size" { + type = string + default = "s-8vcpu-32gb-640gb-intel" +} + +variable "digitalocean_fullnode_size" { + type = string + default = "s-8vcpu-16gb" +} + +variable "digitalocean_regions" { + default = [ + "nyc1", + "sgp1", + "lon1", + "nyc3", + "ams3", + "fra1", + "tor1", + "blr1", + "sfo3", + "syd1" + ] +} + +//////////////////////////////////////////////////////////////////////////////////////// +// LOCALS +//////////////////////////////////////////////////////////////////////////////////////// +locals { + digitalocean_vpcs = { + for region in var.digitalocean_regions : region => { + name = "${var.ethereum_network}-${region}" + region = region + ip_range = cidrsubnet(var.base_cidr_block, 8, index(var.digitalocean_regions, region)) + } + } +} + +locals { + digitalocean_vm_groups = flatten([ + for node in local.digitalocean_nodes : [ + for i in range(0, node.count) : { + group_name = node.name + id = "${node.name}-${node.start_index + i + 1}" + vms = { + "${i + 1}" = { + # Validator range for this instance + val_start = node.validator_start + (i * (node.validator_end - node.validator_start) / node.count) + val_end = min( + node.validator_start + ((i + 1) * (node.validator_end - node.validator_start) / node.count), + node.validator_end + ) + validator_count = node.count > 0 ? (node.validator_end - node.validator_start) / node.count : 0 + + # Builder index for buildoor nodes (start index + instance offset); null = no tag + builder_index = node.builder_start != null ? node.builder_start + i : null + + # Supernode: explicit > bootnode/mev > validator_count >= 128 + supernode = ( + node.supernode != null ? node.supernode : + can(regex("(bootnode|mev)", node.name)) ? true : + (node.count > 0 ? (node.validator_end - node.validator_start) / node.count >= 128 : false) + ) + + region = node.region != null ? node.region : var.digitalocean_regions[i % length(var.digitalocean_regions)] + ipv6 = node.ipv6 + arch = "amd64" + } + } + } + ] + ]) +} + +locals { + digitalocean_default_region = "ams3" + digitalocean_default_size = var.digitalocean_fullnode_size + digitalocean_default_image = "debian-13-x64" + digitalocean_global_tags = [ + "Owner:Devops", + "EthNetwork:${var.ethereum_network}" + ] + + digitalocean_vms = flatten([ + for group in local.digitalocean_vm_groups : [ + for vm_key, vm in group.vms : { + id = group.id + group_key = group.group_name + vm_key = vm_key + + name = group.id + ssh_keys = [data.digitalocean_ssh_key.main.fingerprint] + region = vm.region + image = local.digitalocean_default_image + size = vm.supernode ? var.digitalocean_supernode_size : var.digitalocean_fullnode_size + resize_disk = true + monitoring = true + backups = false + ipv6 = vm.ipv6 + vpc_uuid = digitalocean_vpc.main[vm.region].id + + tags = concat(local.digitalocean_global_tags, [ + "group_name:${group.group_name}", + "val_start:${vm.val_start}", + "val_end:${vm.val_end}", + "supernode:${vm.supernode ? "True" : "False"}", + "arch:${vm.arch}", + ], compact([ + can(regex("bootnode", group.group_name)) ? "bootnode:${var.ethereum_network}" : null, + can(regex("mev-relay", group.group_name)) ? "mev-relay:${var.ethereum_network}" : null, + vm.builder_index != null ? "builder_index:${vm.builder_index}" : null + ])) + } + ] + ]) +} + +//////////////////////////////////////////////////////////////////////////////////////// +// DIGITALOCEAN RESOURCES +//////////////////////////////////////////////////////////////////////////////////////// +data "digitalocean_project" "main" { + name = var.digitalocean_project_name +} + +data "digitalocean_ssh_key" "main" { + name = var.digitalocean_ssh_key_name +} + +resource "digitalocean_vpc" "main" { + for_each = local.digitalocean_vpcs + + name = each.value["name"] + region = each.value["region"] + ip_range = each.value["ip_range"] +} + +resource "digitalocean_droplet" "main" { + for_each = { + for vm in local.digitalocean_vms : vm.id => vm + } + name = "${var.ethereum_network}-${each.value.name}" + region = each.value.region + ssh_keys = each.value.ssh_keys + image = each.value.image + size = each.value.size + resize_disk = each.value.resize_disk + monitoring = each.value.monitoring + backups = each.value.backups + ipv6 = each.value.ipv6 + vpc_uuid = each.value.vpc_uuid + tags = each.value.tags +} + +resource "digitalocean_project_resources" "droplets" { + for_each = digitalocean_droplet.main + project = data.digitalocean_project.main.id + resources = [each.value.urn] +} diff --git a/terraform/devnet-7/firewall.tf b/terraform/devnet-7/firewall.tf new file mode 100644 index 0000000..bfb643d --- /dev/null +++ b/terraform/devnet-7/firewall.tf @@ -0,0 +1,345 @@ +//////////////////////////////////////////////////////////////////////////////////////// +// DIGITALOCEAN FIREWALLS +//////////////////////////////////////////////////////////////////////////////////////// +resource "digitalocean_firewall" "main" { + count = length(local.digitalocean_vms) > 0 ? 1 : 0 + name = "${var.ethereum_network}-nodes" + tags = [ + "EthNetwork:${var.ethereum_network}" + ] + + // SSH + inbound_rule { + protocol = "tcp" + port_range = "22" + source_addresses = ["0.0.0.0/0", "::/0"] + } + + // Allow all inbound ICMP + inbound_rule { + protocol = "icmp" + source_addresses = ["0.0.0.0/0", "::/0"] + } + + // Nginx / Web + inbound_rule { + protocol = "tcp" + port_range = "80" + source_addresses = ["0.0.0.0/0", "::/0"] + } + + inbound_rule { + protocol = "tcp" + port_range = "443" + source_addresses = ["0.0.0.0/0", "::/0"] + } + + // Consensus layer p2p port + inbound_rule { + protocol = "tcp" + port_range = "9000-9001" + source_addresses = ["0.0.0.0/0", "::/0"] + } + inbound_rule { + protocol = "udp" + port_range = "9000-9002" + source_addresses = ["0.0.0.0/0", "::/0"] + } + // Prysm QUIC port (default p2p-quic-port=13000) + inbound_rule { + protocol = "udp" + port_range = "13000" + source_addresses = ["0.0.0.0/0", "::/0"] + } + + // Execution layer p2p Port + inbound_rule { + protocol = "tcp" + port_range = "30303" + source_addresses = ["0.0.0.0/0", "::/0"] + } + inbound_rule { + protocol = "udp" + port_range = "30303" + source_addresses = ["0.0.0.0/0", "::/0"] + } + inbound_rule { + protocol = "tcp" + port_range = "42069" + source_addresses = ["0.0.0.0/0", "::/0"] + } + inbound_rule { + protocol = "udp" + port_range = "42069" + source_addresses = ["0.0.0.0/0", "::/0"] + } + + // Engine rpc-snooper api + inbound_rule { + protocol = "tcp" + port_range = "8961" + source_addresses = ["0.0.0.0/0", "::/0"] + } + + // Allow all outbound traffic + outbound_rule { + protocol = "tcp" + port_range = "1-65535" + destination_addresses = ["0.0.0.0/0", "::/0"] + } + outbound_rule { + protocol = "udp" + port_range = "1-65535" + destination_addresses = ["0.0.0.0/0", "::/0"] + } + outbound_rule { + protocol = "icmp" + destination_addresses = ["0.0.0.0/0", "::/0"] + } + depends_on = [digitalocean_project_resources.droplets] +} + +resource "digitalocean_firewall" "bootnode" { + count = contains(keys(digitalocean_droplet.main), "bootnode-1") ? 1 : 0 + name = "${var.ethereum_network}-nodes-bootnode" + tags = [ + "bootnode:${var.ethereum_network}" + ] + + // DNS + inbound_rule { + protocol = "tcp" + port_range = "53" + source_addresses = ["0.0.0.0/0", "::/0"] + } + inbound_rule { + protocol = "udp" + port_range = "53" + source_addresses = ["0.0.0.0/0", "::/0"] + } + + // Bootnodoor P2P + inbound_rule { + protocol = "tcp" + port_range = "9010" + source_addresses = ["0.0.0.0/0", "::/0"] + } + inbound_rule { + protocol = "udp" + port_range = "9010" + source_addresses = ["0.0.0.0/0", "::/0"] + } + + depends_on = [digitalocean_project_resources.droplets] +} + +resource "digitalocean_firewall" "mev_relay" { + count = contains(keys(digitalocean_droplet.main), "mev-relay-1") ? 1 : 0 + name = "${var.ethereum_network}-nodes-mev-relay" + tags = ["mev-relay:${var.ethereum_network}"] + + // mev-relay ports + inbound_rule { + protocol = "tcp" + port_range = "9060-9062" + source_addresses = ["0.0.0.0/0", "::/0"] + } + depends_on = [digitalocean_project_resources.droplets] +} + +//////////////////////////////////////////////////////////////////////////////////////// +// HETZNER FIREWALLS +//////////////////////////////////////////////////////////////////////////////////////// +resource "hcloud_firewall" "machine_firewall" { + count = local.hetzner_has_servers ? 1 : 0 + name = "${var.ethereum_network}-firewall" + + apply_to { + label_selector = "EthNetwork=${var.ethereum_network}" + } + + // SSH + rule { + description = "Allow SSH" + direction = "in" + protocol = "tcp" + port = "22" + source_ips = ["0.0.0.0/0", "::/0"] + } + + // Allow all inbound ICMP + rule { + description = "Allow all inbound ICMP" + direction = "in" + protocol = "icmp" + source_ips = ["0.0.0.0/0", "::/0"] + } + + // Nginx / Web + rule { + description = "Allow HTTP" + direction = "in" + protocol = "tcp" + port = "80" + source_ips = ["0.0.0.0/0", "::/0"] + } + + rule { + description = "Allow HTTPS" + direction = "in" + protocol = "tcp" + port = "443" + source_ips = ["0.0.0.0/0", "::/0"] + } + + // Consensus layer p2p port + rule { + description = "Allow consensus p2p port TCP" + direction = "in" + protocol = "tcp" + port = "9000-9002" + source_ips = ["0.0.0.0/0", "::/0"] + } + + rule { + description = "Allow consensus p2p port UDP" + direction = "in" + protocol = "udp" + port = "9000-9002" + source_ips = ["0.0.0.0/0", "::/0"] + } + + // Prysm QUIC port (default p2p-quic-port=13000) + rule { + description = "Allow Prysm QUIC port UDP" + direction = "in" + protocol = "udp" + port = "13000" + source_ips = ["0.0.0.0/0", "::/0"] + } + + // Execution layer p2p Port + rule { + description = "Allow execution p2p port TCP" + direction = "in" + protocol = "tcp" + port = "30303" + source_ips = ["0.0.0.0/0", "::/0"] + } + + rule { + description = "Allow execution p2p port UDP" + direction = "in" + protocol = "udp" + port = "30303" + source_ips = ["0.0.0.0/0", "::/0"] + } + + rule { + description = "Allow execution torrent port TCP" + direction = "in" + protocol = "tcp" + port = "42069" + source_ips = ["0.0.0.0/0", "::/0"] + } + + rule { + description = "Allow execution torrent port UDP" + direction = "in" + protocol = "udp" + port = "42069" + source_ips = ["0.0.0.0/0", "::/0"] + } + + // Engine rpc-snooper api + rule { + description = "Allow engine snooper api port TCP" + direction = "in" + protocol = "tcp" + port = "8961" + source_ips = ["0.0.0.0/0", "::/0"] + } + + // Allow all outbound traffic + rule { + description = "Allow all outbound traffic TCP" + direction = "out" + protocol = "tcp" + port = "1-65535" + destination_ips = ["0.0.0.0/0", "::/0"] + } + + rule { + description = "Allow all outbound traffic UDP" + direction = "out" + protocol = "udp" + port = "1-65535" + destination_ips = ["0.0.0.0/0", "::/0"] + } + + rule { + description = "Allow all outbound traffic ICMP" + direction = "out" + protocol = "icmp" + destination_ips = ["0.0.0.0/0", "::/0"] + } +} + +resource "hcloud_firewall" "bootnode_firewall" { + count = contains(keys(hcloud_server.main), "bootnode-1") ? 1 : 0 + name = "${var.ethereum_network}-bootnode-firewall" + + apply_to { + label_selector = "bootnode=${var.ethereum_network}" + } + + // DNS + rule { + description = "Allow DNS UDP" + direction = "in" + protocol = "udp" + port = "53" + source_ips = ["0.0.0.0/0", "::/0"] + } + rule { + description = "Allow DNS TCP" + direction = "in" + protocol = "tcp" + port = "53" + source_ips = ["0.0.0.0/0", "::/0"] + } + + // Bootnodoor P2P + rule { + description = "Allow Bootnodoor P2P port TCP" + direction = "in" + protocol = "tcp" + port = "9010" + source_ips = ["0.0.0.0/0", "::/0"] + } + rule { + description = "Allow Bootnodoor P2P port UDP" + direction = "in" + protocol = "udp" + port = "9010" + source_ips = ["0.0.0.0/0", "::/0"] + } +} + +resource "hcloud_firewall" "mev_relay_firewall" { + count = contains(keys(hcloud_server.main), "mev-relay-1") ? 1 : 0 + name = "${var.ethereum_network}-mev-relay-firewall" + + apply_to { + label_selector = "mev=${var.ethereum_network}" + } + + // mev-relay ports + rule { + description = "Allow MEV Relay ports" + direction = "in" + protocol = "tcp" + port = "9060-9062" + source_ips = ["0.0.0.0/0", "::/0"] + } +} diff --git a/terraform/devnet-7/hetzner.tf b/terraform/devnet-7/hetzner.tf new file mode 100644 index 0000000..c94ebde --- /dev/null +++ b/terraform/devnet-7/hetzner.tf @@ -0,0 +1,218 @@ +//////////////////////////////////////////////////////////////////////////////////////// +// VARIABLES +//////////////////////////////////////////////////////////////////////////////////////// +variable "hcloud_ssh_key_fingerprint" { + type = string + default = "d6:76:2d:9c:5b:33:80:ff:0f:09:a2:10:9b:58:7e:dc" +} + +variable "hetzner_supernode_size" { + type = string + default = "cpx62" +} + +variable "hetzner_fullnode_size" { + type = string + default = "cpx42" +} + +variable "hetzner_regions" { + default = [ + "fsn1", + "nbg1", + "hel1", + ] +} + +//////////////////////////////////////////////////////////////////////////////////////// +// DATA SOURCES +//////////////////////////////////////////////////////////////////////////////////////// +data "hcloud_server_type" "supernode" { + count = local.hetzner_has_servers ? 1 : 0 + name = var.hetzner_supernode_size +} + +data "hcloud_server_type" "fullnode" { + count = local.hetzner_has_servers ? 1 : 0 + name = var.hetzner_fullnode_size +} + +data "hcloud_datacenters" "all" { + count = local.hetzner_has_servers ? 1 : 0 +} + +//////////////////////////////////////////////////////////////////////////////////////// +// LOCALS +//////////////////////////////////////////////////////////////////////////////////////// +locals { + hetzner_has_servers = length(local.hetzner_nodes) > 0 + + required_hetzner_server_type_ids = local.hetzner_has_servers ? toset([ + tostring(data.hcloud_server_type.supernode[0].id), + tostring(data.hcloud_server_type.fullnode[0].id), + ]) : toset([]) + + hetzner_available_locations = local.hetzner_has_servers ? distinct([ + for dc in data.hcloud_datacenters.all[0].datacenters : dc.location.name + if alltrue([for tid in local.required_hetzner_server_type_ids : contains([for id in dc.available_server_type_ids : tostring(id)], tid)]) + ]) : [] + + hetzner_regions_filtered = [ + for r in var.hetzner_regions : r if contains(local.hetzner_available_locations, r) + ] + + hetzner_regions_effective = length(local.hetzner_regions_filtered) > 0 ? local.hetzner_regions_filtered : var.hetzner_regions + + hetzner_network = { + for region in var.hetzner_regions : region => { + name = "${var.ethereum_network}-${region}" + ip_range = cidrsubnet(var.base_cidr_block, 8, index(var.hetzner_regions, region)) + } + } + hetzner_network_subnets = { + for region in var.hetzner_regions : region => { + zone = "eu-central" + ip_range = cidrsubnet(var.base_cidr_block, 8, index(var.hetzner_regions, region)) + } + } +} + +locals { + hetzner_vm_groups = flatten([ + for node in local.hetzner_nodes : [ + for i in range(0, node.count) : { + group_name = node.name + id = "${node.name}-${node.start_index + i + 1}" + vms = { + "${i + 1}" = { + # Validator range for this instance + val_start = node.validator_start + (i * (node.validator_end - node.validator_start) / node.count) + val_end = min( + node.validator_start + ((i + 1) * (node.validator_end - node.validator_start) / node.count), + node.validator_end + ) + validator_count = node.count > 0 ? (node.validator_end - node.validator_start) / node.count : 0 + + # Builder index for buildoor nodes (start index + instance offset); null = no tag + builder_index = node.builder_start != null ? node.builder_start + i : null + + # Supernode: explicit > bootnode/mev > validator_count >= 128 + supernode = ( + node.supernode != null ? node.supernode : + can(regex("(bootnode|mev)", node.name)) ? true : + (node.count > 0 ? (node.validator_end - node.validator_start) / node.count >= 128 : false) + ) + + # Size: explicit > supernode-based default + size = ( + node.size != null ? node.size : + (node.supernode != null ? node.supernode : + can(regex("(bootnode|mev)", node.name)) ? true : + (node.count > 0 ? (node.validator_end - node.validator_start) / node.count >= 128 : false) + ) ? var.hetzner_supernode_size : var.hetzner_fullnode_size + ) + + location = node.location != null ? node.location : local.hetzner_regions_effective[i % length(local.hetzner_regions_effective)] + ipv4_enabled = node.ipv4_enabled + ipv6_enabled = node.ipv6_enabled + } + } + } + ] + ]) +} + +locals { + hcloud_default_location = "nbg1" + hcloud_default_image = "debian-13" + hcloud_default_server_type = var.hetzner_fullnode_size + hcloud_global_labels = [ + "Owner:Devops", + "EthNetwork:${var.ethereum_network}" + ] + + hcloud_vms = flatten([ + for group in local.hetzner_vm_groups : [ + for vm_key, vm in group.vms : { + id = group.id + group_key = group.group_name + vm_key = vm_key + + name = group.id + ipv4_enabled = vm.ipv4_enabled + ipv6_enabled = vm.ipv6_enabled + ssh_keys = local.hetzner_has_servers ? [data.hcloud_ssh_key.main[0].id] : [] + location = vm.location + image = local.hcloud_default_image + server_type = vm.size + backups = false + + # Architecture: cax* = ARM64, everything else = AMD64 + arch = can(regex("^cax", vm.size)) ? "arm64" : "amd64" + + labels = concat(local.hcloud_global_labels, [ + "group_name:${group.group_name}", + "val_start:${vm.val_start}", + "val_end:${vm.val_end}", + "supernode:${vm.supernode ? "True" : "False"}", + "arch:${can(regex("^cax", vm.size)) ? "arm64" : "amd64"}", + ], compact([ + can(regex("bootnode", group.group_name)) ? "bootnode:${var.ethereum_network}" : null, + can(regex("mev-relay", group.group_name)) ? "mev:${var.ethereum_network}" : null, + vm.builder_index != null ? "builder_index:${vm.builder_index}" : null + ])) + } + ] + ]) +} + +//////////////////////////////////////////////////////////////////////////////////////// +// HETZNER RESOURCES +//////////////////////////////////////////////////////////////////////////////////////// +resource "hcloud_network" "main" { + for_each = local.hetzner_has_servers ? local.hetzner_network : {} + name = each.value.name + ip_range = each.value.ip_range +} + +resource "hcloud_network_subnet" "main" { + for_each = local.hetzner_has_servers ? local.hetzner_network_subnets : {} + network_id = hcloud_network.main[each.key].id + type = "cloud" + network_zone = each.value.zone + ip_range = each.value.ip_range +} + +data "hcloud_ssh_key" "main" { + count = local.hetzner_has_servers ? 1 : 0 + fingerprint = var.hcloud_ssh_key_fingerprint +} + +resource "hcloud_server" "main" { + for_each = { + for vm in local.hcloud_vms : vm.id => vm + } + name = "${var.ethereum_network}-${each.value.name}" + image = each.value.image + server_type = each.value.server_type + location = each.value.location + ssh_keys = each.value.ssh_keys + backups = each.value.backups + labels = { for label in each.value.labels : split(":", label)[0] => split(":", label)[1] } + public_net { + ipv4_enabled = each.value.ipv4_enabled + ipv6_enabled = each.value.ipv6_enabled + } + + lifecycle { + ignore_changes = [location] + } +} + +resource "hcloud_server_network" "main" { + for_each = { + for vm in local.hcloud_vms : vm.id => vm + } + server_id = hcloud_server.main[each.key].id + network_id = hcloud_network.main[hcloud_server.main[each.key].location].id +} diff --git a/terraform/devnet-7/main.tf b/terraform/devnet-7/main.tf new file mode 100644 index 0000000..9c301b2 --- /dev/null +++ b/terraform/devnet-7/main.tf @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////////////////// +// TERRAFORM PROVIDERS & BACKEND +//////////////////////////////////////////////////////////////////////////////////////// +terraform { + required_providers { + digitalocean = { + source = "digitalocean/digitalocean" + version = "~> 2.28" + } + cloudflare = { + source = "cloudflare/cloudflare" + version = "~> 3.0" + } + hcloud = { + source = "hetznercloud/hcloud" + version = "~> 1.42.1" + } + } +} + +terraform { + backend "s3" { + skip_credentials_validation = true + skip_metadata_api_check = true + endpoints = { s3 = "https://fra1.digitaloceanspaces.com" } + skip_requesting_account_id = true + skip_s3_checksum = true + region = "us-east-1" + bucket = "merge-testnets" + key = "infrastructure/devnet-7/terraform.tfstate" + } +} + +provider "digitalocean" { + http_retry_max = 20 +} + +provider "cloudflare" { + api_token = var.cloudflare_api_token +} + +provider "hcloud" { + token = var.glamsterdam_hcloud_token +} + +//////////////////////////////////////////////////////////////////////////////////////// +// VARIABLES +//////////////////////////////////////////////////////////////////////////////////////// +variable "cloudflare_api_token" { + type = string + sensitive = true + description = "Cloudflare API Token" +} + +variable "glamsterdam_hcloud_token" { + type = string + sensitive = true + default = "" + description = "Hetzner Cloud API Token (optional if not using Hetzner)" +} + +variable "ethereum_network" { + type = string + default = "glamsterdam-devnet-7" +} + +variable "base_cidr_block" { + default = "10.27.0.0/16" +} + +//////////////////////////////////////////////////////////////////////////////////////// +// LOCALS +//////////////////////////////////////////////////////////////////////////////////////// +locals { + # Normalize node entries with defaults and calculate starting index for continuous numbering + nodes_normalized = [ + for idx, node in var.nodes : { + name = node.name + count = node.count + cloud = node.cloud + validator_start = try(node.validator_start, 0) + validator_end = try(node.validator_end, 0) + builder_start = try(node.builder_start, null) + size = try(node.size, null) + region = try(node.region, null) + location = try(node.location, try(node.region, null)) + supernode = try(node.supernode, null) + ipv6 = try(node.ipv6, true) + ipv4_enabled = try(node.ipv4_enabled, true) + ipv6_enabled = try(node.ipv6_enabled, true) + # Calculate starting index: sum of counts from all previous entries with same name + start_index = sum(concat([for i, n in var.nodes : n.count if i < idx && n.name == node.name], [0])) + } + ] + + # Filter by cloud provider (only nodes with count > 0) + digitalocean_nodes = [for n in local.nodes_normalized : n if n.cloud == "digitalocean" && n.count > 0] + hetzner_nodes = [for n in local.nodes_normalized : n if n.cloud == "hetzner" && n.count > 0] +} diff --git a/terraform/devnet-7/nodes.tf b/terraform/devnet-7/nodes.tf new file mode 100644 index 0000000..bf5ed63 --- /dev/null +++ b/terraform/devnet-7/nodes.tf @@ -0,0 +1,81 @@ +######################################################################################## +# NODE DEFINITIONS +# +# Define your fleet as a list of node entries. Each entry supports: +# +# Required: +# - name : Node type (e.g., "lighthouse-geth-super", "bootnode") +# - count : Number of instances +# - cloud : "digitalocean" or "hetzner" +# +# Optional: +# - validator_start : First validator index (default: 0) +# - validator_end : Last validator index (default: 0) +# - size : Instance size override (provider-specific) +# - region : Region override (digitalocean) or location (hetzner) +# - supernode : Force supernode=true/false (auto-detected from name) +# - builder_start : First builder index (buildoor nodes only). Exposes a +# builder_index=N server tag and inventory var per instance. +# +# Examples: +# { name = "bootnode", count = 1, cloud = "digitalocean" } +# { name = "lighthouse-geth-super", count = 2, cloud = "hetzner", validator_start = 0, validator_end = 200 } +# { name = "mev-relay", count = 1, cloud = "hetzner", size = "ccx53" } +# +######################################################################################## + +variable "nodes" { + description = "List of node definitions for the devnet" + default = [ + { name = "bootnode", count = 1, cloud = "digitalocean" }, + # Buildoor + { name = "buildoor-prysm-ethrex", count = 1, cloud = "digitalocean", builder_start = 0 }, + { name = "buildoor-lighthouse-geth", count = 1, cloud = "digitalocean", builder_start = 1 }, + { name = "buildoor-lodestar-ethrex", count = 1, cloud = "digitalocean", builder_start = 2 }, + { name = "buildoor-teku-nethermind", count = 1, cloud = "digitalocean", builder_start = 3 }, + + # Ethrex + { name = "prysm-ethrex", count = 1, cloud = "digitalocean", validator_start = 0, validator_end = 200 }, + { name = "lodestar-ethrex", count = 1, cloud = "digitalocean", validator_start = 200, validator_end = 400 }, + { name = "lighthouse-ethrex", count = 1, cloud = "digitalocean", validator_start = 400, validator_end = 600 }, + { name = "nimbus-ethrex", count = 1, cloud = "digitalocean", validator_start = 600, validator_end = 800 }, + { name = "teku-ethrex", count = 1, cloud = "digitalocean", validator_start = 800, validator_end = 1000 }, + + # Geth + { name = "prysm-geth", count = 1, cloud = "digitalocean", validator_start = 1000, validator_end = 1200 }, + { name = "lodestar-geth", count = 1, cloud = "digitalocean", validator_start = 1200, validator_end = 1400 }, + { name = "lighthouse-geth", count = 1, cloud = "digitalocean", validator_start = 1400, validator_end = 1600 }, + { name = "nimbus-geth", count = 1, cloud = "digitalocean", validator_start = 1600, validator_end = 1800 }, + { name = "teku-geth", count = 1, cloud = "digitalocean", validator_start = 1800, validator_end = 2000 }, + + # Nethermind + { name = "prysm-nethermind", count = 1, cloud = "digitalocean", validator_start = 2000, validator_end = 2200 }, + { name = "lodestar-nethermind", count = 1, cloud = "digitalocean", validator_start = 2200, validator_end = 2400 }, + { name = "lighthouse-nethermind", count = 1, cloud = "digitalocean", validator_start = 2400, validator_end = 2600 }, + { name = "nimbus-nethermind", count = 1, cloud = "digitalocean", validator_start = 2600, validator_end = 2800 }, + { name = "teku-nethermind", count = 1, cloud = "digitalocean", validator_start = 2800, validator_end = 3000 }, + + ] + + validation { + condition = alltrue([ + for n in var.nodes : + try(n.validator_start, 0) >= 0 && try(n.validator_start, 0) <= try(n.validator_end, 0) + ]) + error_message = "Each node must satisfy 0 <= validator_start <= validator_end. Omit both fields (or set both to 0) for nodes without validators." + } + + validation { + condition = alltrue(flatten([ + for i, a in var.nodes : [ + for j, b in var.nodes : + i >= j || + try(a.validator_end, 0) == 0 || + try(b.validator_end, 0) == 0 || + try(a.validator_end, 0) <= try(b.validator_start, 0) || + try(b.validator_end, 0) <= try(a.validator_start, 0) + ] + ])) + error_message = "Validator ranges overlap between nodes. Each [validator_start, validator_end) interval must be disjoint from every other node's interval." + } +} diff --git a/terraform/devnet-7/outputs.tf b/terraform/devnet-7/outputs.tf new file mode 100644 index 0000000..d252f29 --- /dev/null +++ b/terraform/devnet-7/outputs.tf @@ -0,0 +1,120 @@ +//////////////////////////////////////////////////////////////////////////////////////// +// GENERATED FILES AND OUTPUTS +//////////////////////////////////////////////////////////////////////////////////////// + +resource "local_file" "ansible_inventory" { + content = templatefile("ansible_inventory.tmpl", + { + ethereum_network_name = "${var.ethereum_network}" + groups = merge( + { for group in local.digitalocean_vm_groups : "${group.group_name}" => true... }, + { for group in local.hetzner_vm_groups : "${group.group_name}" => true... }, + ) + hosts = merge( + { + for key, server in digitalocean_droplet.main : "do.${key}" => { + ip = "${server.ipv4_address}" + ipv6 = try(server.ipv6_address, "none") + group = try([for tag in tolist(server.tags) : split(":", tag)[1] if can(regex("^group_name:", tag))][0], "unknown") + validator_start = try([for tag in tolist(server.tags) : split(":", tag)[1] if can(regex("^val_start:", tag))][0], 0) + validator_end = try([for tag in tolist(server.tags) : split(":", tag)[1] if can(regex("^val_end:", tag))][0], 0) + supernode = try(title([for tag in tolist(server.tags) : split(":", tag)[1] if can(regex("^supernode:", tag))][0]), "undefined") + arch = try([for tag in tolist(server.tags) : split(":", tag)[1] if can(regex("^arch:", tag))][0], "amd64") + builder_index = try([for tag in tolist(server.tags) : split(":", tag)[1] if can(regex("^builder_index:", tag))][0], "") + tags = "${server.tags}" + hostname = "${split(".", key)[0]}" + cloud = "digitalocean" + region = "${server.region}" + } + }, + { + for key, server in hcloud_server.main : "${key}" => { + ip = coalesce(server.ipv4_address, (try(server.ipv6_address, ""))) + ipv6 = coalesce(server.ipv6_address, "") + group = server.labels.group_name + validator_start = server.labels.val_start + validator_end = server.labels.val_end + supernode = server.labels.supernode + arch = server.labels.arch + builder_index = try(server.labels.builder_index, "") + tags = server.labels + hostname = split(".", key)[0] + cloud = "hetzner" + region = server.datacenter + } + } + ) + } + ) + filename = "../../ansible/inventories/${join("-", slice(split("-", var.ethereum_network), length(split("-", var.ethereum_network)) - 2, length(split("-", var.ethereum_network))))}/inventory.ini" +} + +locals { + ssh_config_path = pathexpand("~/.ssh/config.d/ssh_config.${var.ethereum_network}") +} + +resource "local_file" "ssh_config" { + content = templatefile("${path.module}/ssh_config.tmpl", + { + ethereum_network = var.ethereum_network + hosts = merge( + { + for key, server in digitalocean_droplet.main : "${var.ethereum_network}-${key}" => { + hostname = server.ipv4_address + private_ip = server.ipv4_address_private + name = key + user = "devops" + } + }, + { + for key, server in hcloud_server.main : "${var.ethereum_network}-${key}" => { + hostname = coalesce(server.ipv4_address, (try(server.ipv6_address, ""))) + private_ip = try(hcloud_server_network.main[key].ip, "") + name = key + user = "devops" + } + } + ) + } + ) + filename = local.ssh_config_path + + depends_on = [digitalocean_droplet.main, hcloud_server.main] + + lifecycle { + create_before_destroy = true + } +} + +resource "null_resource" "ssh_config_cleanup" { + triggers = { + ssh_config_path = local.ssh_config_path + } + + provisioner "local-exec" { + when = destroy + command = "rm -f ${self.triggers.ssh_config_path} || true" + } + + depends_on = [local_file.ssh_config] +} + +output "ssh_config_file" { + value = "SSH config generated at: ${local.ssh_config_path}" + description = "Path to the generated SSH config file" +} + +output "digitalocean_server_count" { + value = length(digitalocean_droplet.main) + description = "Number of DigitalOcean servers created" +} + +output "hetzner_server_count" { + value = length(hcloud_server.main) + description = "Number of Hetzner servers created" +} + +output "total_server_count" { + value = length(digitalocean_droplet.main) + length(hcloud_server.main) + description = "Total number of servers created across all providers" +} diff --git a/terraform/devnet-7/ssh_config.tmpl b/terraform/devnet-7/ssh_config.tmpl new file mode 100644 index 0000000..d2394b4 --- /dev/null +++ b/terraform/devnet-7/ssh_config.tmpl @@ -0,0 +1,16 @@ +# SSH Config for ${ethereum_network} devnet +# Generated by Terraform +# Usage: Include this file in your ~/.ssh/config with: +# Include /path/to/this/ssh_config +# Or copy entries to your main SSH config file + +%{ for host_key, host in hosts ~} +Host ${host_key} + HostName ${host.hostname} + User ${host.user} + Port 22 + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + LogLevel ERROR + +%{ endfor ~}