diff --git a/.github/workflows/test-molecule.yml b/.github/workflows/test-molecule.yml index 3ea31581f..4b25f5aa2 100644 --- a/.github/workflows/test-molecule.yml +++ b/.github/workflows/test-molecule.yml @@ -195,6 +195,18 @@ jobs: {role: "manage-service", test: "monitor-nimbus"}, {role: "manage-service", test: "monitor-prysm"}, {role: "manage-service", test: "monitor-teku"}, + {role: "manage-service", test: "mevboost-besu-lighthouse"}, + {role: "manage-service", test: "mevboost-besu-nimbus"}, + {role: "manage-service", test: "mevboost-besu-prysm"}, + {role: "manage-service", test: "mevboost-besu-teku"}, + {role: "manage-service", test: "mevboost-geth-lighthouse"}, + {role: "manage-service", test: "mevboost-geth-nimbus"}, + {role: "manage-service", test: "mevboost-geth-prysm"}, + {role: "manage-service", test: "mevboost-geth-teku"}, + {role: "manage-service", test: "mevboost-nethermind-lighthouse"}, + {role: "manage-service", test: "mevboost-nethermind-nimbus"}, + {role: "manage-service", test: "mevboost-nethermind-prysm"}, + {role: "manage-service", test: "mevboost-nethermind-teku"}, {role: "manage-service", test: "besu-lighthouse"}, {role: "manage-service", test: "besu-nimbus"}, {role: "manage-service", test: "besu-prysm"}, diff --git a/controls/defaults/stereum_defaults.yaml b/controls/defaults/stereum_defaults.yaml index 9c40cf14d..c70f8bb6c 100644 --- a/controls/defaults/stereum_defaults.yaml +++ b/controls/defaults/stereum_defaults.yaml @@ -10,15 +10,18 @@ stereum_static: install: false versions: # consensus clients - lighthouse: v2.5.1 - nimbus: multiarch-v22.7.0 - teku: "22.8.0" - prysm: v2.1.3 + lighthouse: v3.1.0 + nimbus: multiarch-v22.8.2 + teku: "22.9.0" + prysm: v3.1.0 # execution clients - geth: v1.10.21 - besu: "22.7.0-RC2" - nethermind: "1.13.5" + geth: v1.10.23 + besu: "22.7.1" + nethermind: "1.14.0" + + # mevboost + mevboost: v0.8.2 # ssv ssv_network: v0.3.0 @@ -29,3 +32,7 @@ stereum_static: node_exporter: v1.3.1 prometheus: v2.36.2 notifications: v1.1.0 + + # mevboost - relay + relay: + goerli: https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@builder-relay-goerli.flashbots.net diff --git a/controls/roles/manage-service/molecule/besu-prysm/converge.yml b/controls/roles/manage-service/molecule/besu-prysm/converge.yml index c1ccb8383..bff997f9a 100644 --- a/controls/roles/manage-service/molecule/besu-prysm/converge.yml +++ b/controls/roles/manage-service/molecule/besu-prysm/converge.yml @@ -85,7 +85,7 @@ --datadir=/opt/app/beacon --prater=true --genesis-state=/opt/app/genesis/prysm-prater-genesis.ssz - --http-web3provider=http://stereum-{{ besu_service }}:8551 + --execution-endpoint=http://stereum-{{ besu_service }}:8551 --jwt-secret=/engine.jwt user: "2000" volumes: diff --git a/controls/roles/manage-service/molecule/geth-prysm/converge.yml b/controls/roles/manage-service/molecule/geth-prysm/converge.yml index 2d6d24f6b..ad127db5b 100644 --- a/controls/roles/manage-service/molecule/geth-prysm/converge.yml +++ b/controls/roles/manage-service/molecule/geth-prysm/converge.yml @@ -79,7 +79,7 @@ --rpc-host=0.0.0.0 --grpc-gateway-host=0.0.0.0 --p2p-max-peers=100 - --http-web3provider=http://stereum-{{ geth_service }}:8551 + --execution-endpoint=http://stereum-{{ geth_service }}:8551 --monitoring-port=8080 --monitoring-host=0.0.0.0 --jwt-secret=/engine.jwt diff --git a/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/converge.yml b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/converge.yml new file mode 100644 index 000000000..5fc43129e --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/converge.yml @@ -0,0 +1,161 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + lighthouse_beacon_service: 5bc8cb90-1909-11ed-9142-cf15cf490a91 + lighthouse_validator_service: 5e04445c-1909-11ed-b0ac-1b18212b17aa + besu_service: 5c716d90-1909-11ed-986f-931342a7fe9d + mevboost_service: 5c9541ac-1909-11ed-8585-a373c154e6c6 + + tasks: + # besu service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: BesuService + id: "{{ besu_service }}" + image: hyperledger/besu:{{ stereum_static.defaults.versions.besu }} + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["besu"] + env: + JAVA_OPTS: -Xmx4g + command: + - --network=goerli + - --data-path=/opt/app/data + - --data-storage-format=BONSAI + - --sync-mode=X_SNAP + - --host-allowlist=* + - --rpc-http-enabled=true + - --rpc-http-host=0.0.0.0 + - --rpc-http-cors-origins=* + - --rpc-http-api=ETH,NET,WEB3 + - --rpc-ws-enabled=true + - --rpc-ws-api=WEB3,ETH,NET + - --rpc-ws-host=0.0.0.0 + - --engine-rpc-enabled=true + - --engine-rpc-port=8551 + - --engine-jwt-enabled=true + - --engine-jwt-secret=/engine.jwt + - --engine-host-allowlist=* + user: "2000" + volumes: + - "/opt/app/services/{{ besu_service }}/data:/opt/app/data" + - "/opt/app/services/{{ besu_service }}/engine.jwt:/engine.jwt" + + # lh beacon service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: LighthouseBeaconService + id: "{{ lighthouse_beacon_service }}" + image: sigp/lighthouse:{{ stereum_static.defaults.versions.lighthouse }} + env: {} + ports: + - 0.0.0.0:9000:9000/tcp + - 0.0.0.0:9000:9000/udp + command: + - lighthouse + - --network=goerli + - --debug-level=info + - beacon_node + - --datadir=/opt/app/beacon + - --http + - --http-address=0.0.0.0 + - --metrics + - --metrics-address=0.0.0.0 + - --execution-endpoint=http://stereum-{{ besu_service }}:8551 + - --execution-jwt=/engine.jwt + - --builder=http://stereum-{{ mevboost_service }}:18550 + entrypoint: [] + user: "2000" + volumes: + - "/opt/app/services/{{ lighthouse_beacon_service }}/beacon/lighthouse:/opt/app/beacon" + - "/opt/app/services/{{ lighthouse_beacon_service }}/slasher:/opt/app/slasher" + - "/opt/app/services/{{ besu_service }}/engine.jwt:/engine.jwt" + + # lh validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: LighthouseValidatorService + id: "{{ lighthouse_validator_service }}" + image: sigp/lighthouse:{{ stereum_static.defaults.versions.lighthouse }} + env: {} + command: + - lighthouse + - vc + - --debug-level=info + - --network=goerli + - --beacon-nodes=http://stereum-{{ lighthouse_beacon_service }}:5052 + - --datadir=/opt/app/validator + - --metrics + - --metrics-address=0.0.0.0 + - --suggested-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + - --builder-proposals + entrypoint: [] + user: "2000" + volumes: + - "/opt/app/services/{{ lighthouse_validator_service }}/validator:/opt/app/validator" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/create.yml b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/destroy.yml b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/molecule.yml b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/molecule.yml new file mode 100644 index 000000000..897d9c583 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-besu-lighthouse--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-besu-lighthouse--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/playbook.yml b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/prepare.yml b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/prepare.yml new file mode 100644 index 000000000..1473c60cd --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/prepare.yml @@ -0,0 +1,30 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install python for Ansible (Ubuntu) + apt: + update_cache: yes + name: pip + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/verify.yml b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/verify.yml new file mode 100644 index 000000000..a19166e00 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-lighthouse/verify.yml @@ -0,0 +1,26 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-5c9541ac-1909-11ed-8585-a373c154e6c6" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # lighthouse beacon logs + - name: Lighthouse beacon + command: "docker logs stereum-5bc8cb90-1909-11ed-9142-cf15cf490a91" + register: lighthouse_beacon + until: + - lighthouse_beacon.stderr is search("The execution endpoint is connected and configured, however it is not yet synced") + - lighthouse_beacon.stderr is not search("Error connecting to eth1 node endpoint") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-nimbus/converge.yml b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/converge.yml new file mode 100644 index 000000000..22f5c3cf7 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/converge.yml @@ -0,0 +1,140 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + besu_service: 607da19e-2316-11ed-99a6-538aeb4affb0 + nimbus_service: 60dab730-2316-11ed-87e9-33da6b458541 + mevboost_service: 610ebb66-2316-11ed-b44b-9f89e153686b + + tasks: + # besu service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: BesuService + id: "{{ besu_service }}" + image: hyperledger/besu:{{ stereum_static.defaults.versions.besu }} + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["besu"] + env: + JAVA_OPTS: -Xmx4g + command: + - --network=goerli + - --data-path=/opt/app/data + - --data-storage-format=BONSAI + - --sync-mode=X_SNAP + - --host-allowlist=* + - --rpc-http-enabled=true + - --rpc-http-host=0.0.0.0 + - --rpc-http-cors-origins=* + - --rpc-http-api=ETH,NET,WEB3 + - --rpc-ws-enabled=true + - --rpc-ws-api=WEB3,ETH,NET + - --rpc-ws-host=0.0.0.0 + - --engine-rpc-enabled=true + - --engine-rpc-port=8551 + - --engine-jwt-enabled=true + - --engine-jwt-secret=/engine.jwt + - --engine-host-allowlist=* + user: "2000" + volumes: + - "/opt/app/services/{{ besu_service }}/data:/opt/app/data" + - "/opt/app/services/{{ besu_service }}/engine.jwt:/engine.jwt" + + # nimbus beacon & validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + - name: Create api-token file + copy: + content: "{{ lookup('password', '/dev/null', seed=inventory_hostname) }}" + dest: /opt/app/services/{{ nimbus_service }}/validator/api-token.txt + force: no + become: yes + - name: Waiting for the services to start properly + pause: + seconds: 15 + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: NimbusBeaconValidatorService + id: "{{ nimbus_service }}" + image: statusim/nimbus-eth2:{{ stereum_static.defaults.versions.nimbus }} + ports: + - 0.0.0.0:9000:9000/tcp + - 0.0.0.0:9000:9000/udp + env: {} + entrypoint: ["/home/user/nimbus-eth2/build/nimbus_beacon_node"] + command: + - --network=goerli + - --data-dir=/opt/app/beacon + - --validators-dir=/opt/app/validators + - --secrets-dir=/opt/app/secrets + - --web3-url=http://stereum-{{ besu_service }}:8551 + - --tcp-port=9000 + - --udp-port=9000 + - --jwt-secret=/engine.jwt + - --suggested-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + - --keymanager + - --keymanager-address=0.0.0.0 + - --keymanager-token-file=/opt/app/validators/api-token.txt + - --payload-builder=on + - --payload-builder-url=http://stereum-{{ mevboost_service }}:18550 + user: "2000" + + volumes: + - "/opt/app/services/{{ nimbus_service }}/beacon:/opt/app/beacon" + - "/opt/app/services/{{ nimbus_service }}/validator:/opt/app/validators" + - "/opt/app/services/{{ nimbus_service }}/validator/secrets:/opt/app/secrets" + - "/opt/app/services/{{ besu_service }}/engine.jwt:/engine.jwt" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-nimbus/create.yml b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-nimbus/destroy.yml b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-nimbus/molecule.yml b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/molecule.yml new file mode 100644 index 000000000..bc2c52b15 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-besu-nimbus--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-besu-nimbus--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-nimbus/playbook.yml b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-nimbus/prepare.yml b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/prepare.yml new file mode 100644 index 000000000..1473c60cd --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/prepare.yml @@ -0,0 +1,30 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install python for Ansible (Ubuntu) + apt: + update_cache: yes + name: pip + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-nimbus/verify.yml b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/verify.yml new file mode 100644 index 000000000..e481340a9 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-nimbus/verify.yml @@ -0,0 +1,26 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-610ebb66-2316-11ed-b44b-9f89e153686b" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # nimbus logs + - name: Nimbus beacon + command: "docker logs stereum-60dab730-2316-11ed-87e9-33da6b458541" + register: nimbus + until: + - nimbus.stdout is search("Established connection to execution layer") + - nimbus.stdout is not search("Payload builder REST client setup failed") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-prysm/converge.yml b/controls/roles/manage-service/molecule/mevboost-besu-prysm/converge.yml new file mode 100644 index 000000000..e6272e25b --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-prysm/converge.yml @@ -0,0 +1,213 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + prysm_beacon_service: 6beef652-2340-11ed-95a3-27ae32be4eeb + prysm_validator_service: 6c14fbea-2340-11ed-b32c-934fd03b47ac + besu_service: 6c3f864e-2340-11ed-b88f-6ba023a56382 + mevboost_service: 6c68baaa-2340-11ed-9102-3f79d0ec2539 + + tasks: + # besu service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: BesuService + id: "{{ besu_service }}" + image: hyperledger/besu:{{ stereum_static.defaults.versions.besu }} + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["besu"] + env: + JAVA_OPTS: -Xmx4g + command: + - --network=goerli + - --data-path=/opt/app/data + - --data-storage-format=BONSAI + - --sync-mode=X_SNAP + - --host-allowlist=* + - --rpc-http-enabled=true + - --rpc-http-host=0.0.0.0 + - --rpc-http-cors-origins=* + - --rpc-http-api=ETH,NET,WEB3 + - --rpc-ws-enabled=true + - --rpc-ws-api=WEB3,ETH,NET + - --rpc-ws-host=0.0.0.0 + - --engine-rpc-enabled=true + - --engine-rpc-port=8551 + - --engine-jwt-enabled=true + - --engine-jwt-secret=/engine.jwt + - --engine-host-allowlist=* + user: "2000" + volumes: + - "/opt/app/services/{{ besu_service }}/data:/opt/app/data" + - "/opt/app/services/{{ besu_service }}/engine.jwt:/engine.jwt" + + # prysm beacon service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: PrysmBeaconService + id: "{{ prysm_beacon_service }}" + image: "prysmaticlabs/prysm-beacon-chain:{{ stereum_static.defaults.versions.prysm }}" + ports: + - 0.0.0.0:12000:12000/udp + - 0.0.0.0:13000:13000/tcp + env: {} + entrypoint: [] + command: | + /app/cmd/beacon-chain/beacon-chain + --accept-terms-of-use=true + --datadir=/opt/app/beacon + --goerli=true + --block-batch-limit=512 + --genesis-state=/opt/app/genesis/prysm-prater-genesis.ssz + --rpc-host=0.0.0.0 + --execution-endpoint=http://stereum-{{ besu_service }}:8551 + --jwt-secret=/engine.jwt + --http-mev-relay=http://stereum-{{ mevboost_service }}:18550 + user: "2000" + volumes: + - "/opt/app/services/{{ prysm_beacon_service }}/prysm/beacon:/opt/app/beacon" + - "/opt/app/services/{{ prysm_beacon_service }}/genesis:/opt/app/genesis" + - "/opt/app/services/{{ besu_service }}/engine.jwt:/engine.jwt" + + # prysm validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + - name: Generate wallet's password + command: uuidgen + register: wallet_password + changed_when: false + become: yes + - name: Set wallet password + copy: + content: "{{ wallet_password.stdout }}" + dest: "/opt/app/services/{{ prysm_validator_service }}/data/passwords/wallet-password" + owner: "2000" + group: "2000" + mode: 0700 + become: yes + - name: Prysm - Create wallet for account(s) + command: bash -c "docker exec stereum-{{ prysm_validator_service }} + /app/cmd/validator/validator + wallet create + --wallet-dir=/opt/app/data/wallets + --wallet-password-file=/opt/app/data/passwords/wallet-password + --accept-terms-of-use + --keymanager-kind=direct + --prater" + changed_when: false + become: yes + - name: Prysm - Set wallet permission + ansible.builtin.file: + path: "/opt/app/services/{{ prysm_validator_service }}/data/wallets" + state: directory + recurse: yes + owner: "2000" + group: "2000" + become: yes + - name: Stop beacon & validator service + docker_container: + name: "{{ item }}" + state: stopped + become: yes + with_items: + - "stereum-{{ prysm_beacon_service }}" + - "stereum-{{ prysm_validator_service }}" + - name: Restart beacon & validator service + command: "docker start {{ item }}" + changed_when: false + become: yes + with_items: + - "stereum-{{ prysm_beacon_service }}" + - "stereum-{{ prysm_validator_service }}" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: PrysmValidatorService + id: "{{ prysm_validator_service }}" + image: "prysmaticlabs/prysm-validator:{{ stereum_static.defaults.versions.prysm }}" + env: {} + entrypoint: [] + command: + /app/cmd/validator/validator + --accept-terms-of-use=true + --beacon-rpc-provider="stereum-{{ prysm_beacon_service }}:4000" + --beacon-rpc-gateway-provider="stereum-{{ prysm_beacon_service }}:3500" + --web + --goerli=true + --datadir=/opt/app/data/db + --wallet-dir=/opt/app/data/wallets + --wallet-password-file=/opt/app/data/passwords/wallet-password + --monitoring-host=0.0.0.0 + --grpc-gateway-port=7500 + --grpc-gateway-host=0.0.0.0 + --grpc-gateway-corsdomain="*" + --enable-builder=true + --suggested-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + user: "2000" + volumes: + - "/opt/app/services/{{ prysm_validator_service }}/data/db:/opt/app/data/db" + - "/opt/app/services/{{ prysm_validator_service }}/data/wallets:/opt/app/data/wallets" + - "/opt/app/services/{{ prysm_validator_service }}/data/passwords:/opt/app/data/passwords" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-prysm/create.yml b/controls/roles/manage-service/molecule/mevboost-besu-prysm/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-prysm/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-prysm/destroy.yml b/controls/roles/manage-service/molecule/mevboost-besu-prysm/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-prysm/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-prysm/molecule.yml b/controls/roles/manage-service/molecule/mevboost-besu-prysm/molecule.yml new file mode 100644 index 000000000..b161c257c --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-prysm/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-besu-prysm--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-besu-prysm--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-prysm/playbook.yml b/controls/roles/manage-service/molecule/mevboost-besu-prysm/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-prysm/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-prysm/prepare.yml b/controls/roles/manage-service/molecule/mevboost-besu-prysm/prepare.yml new file mode 100644 index 000000000..1473c60cd --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-prysm/prepare.yml @@ -0,0 +1,30 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install python for Ansible (Ubuntu) + apt: + update_cache: yes + name: pip + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-prysm/verify.yml b/controls/roles/manage-service/molecule/mevboost-besu-prysm/verify.yml new file mode 100644 index 000000000..a7037f35e --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-prysm/verify.yml @@ -0,0 +1,27 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-6c68baaa-2340-11ed-9102-3f79d0ec2539" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # prysm beacon logs + - name: prysm beacon + command: "docker logs stereum-6beef652-2340-11ed-95a3-27ae32be4eeb" + register: prysm_beacon + until: + - prysm_beacon.stderr is search("Builder has been configured") + - prysm_beacon.stderr is search("Connected to new endpoint") + - prysm_beacon.stderr is not search("Could not connect to execution endpoint") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-teku/converge.yml b/controls/roles/manage-service/molecule/mevboost-besu-teku/converge.yml new file mode 100644 index 000000000..52981d04e --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-teku/converge.yml @@ -0,0 +1,161 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + teku_service: c5a67b18-1fe2-11ed-a441-33f8d671fdfa + besu_service: c5d0c242-1fe2-11ed-9d4a-0fce20fa52f8 + mevboost_service: c5fa81c2-1fe2-11ed-8fc8-4bcc92882234 + + tasks: + # besu service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: BesuService + id: "{{ besu_service }}" + image: hyperledger/besu:{{ stereum_static.defaults.versions.besu }} + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["besu"] + env: + JAVA_OPTS: -Xmx4g + command: + - --network=goerli + - --data-path=/opt/app/data + - --data-storage-format=BONSAI + - --sync-mode=X_SNAP + - --host-allowlist=* + - --rpc-http-enabled=true + - --rpc-http-host=0.0.0.0 + - --rpc-http-cors-origins=* + - --rpc-http-api=ETH,NET,WEB3 + - --rpc-ws-enabled=true + - --rpc-ws-api=WEB3,ETH,NET + - --rpc-ws-host=0.0.0.0 + - --engine-rpc-enabled=true + - --engine-rpc-port=8551 + - --engine-jwt-enabled=true + - --engine-jwt-secret=/engine.jwt + - --engine-host-allowlist=* + user: "2000" + volumes: + - "/opt/app/services/{{ besu_service }}/data:/opt/app/data" + - "/opt/app/services/{{ besu_service }}/engine.jwt:/engine.jwt" + + # teku beacon & validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + + - name: "Include manage-service" + include_role: + name: "manage-service" + + - name: Create keystore password + copy: + content: "{{ lookup('password', '/dev/null', seed=inventory_hostname) }}" + dest: /opt/app/services/{{ teku_service }}/data/teku_api_password.txt + force: no + become: yes + + - name: Get teku-api password + slurp: + src: "/opt/app/services/{{ teku_service }}/data/teku_api_password.txt" + register: teku_api_password + become: yes + + - name: Set variable + set_fact: + api_password: "{{ teku_api_password.content | b64decode | trim }}" + + - name: Create keystore file + command: bash -c "keytool -genkeypair + -keystore teku_api_keystore + -storetype PKCS12 + -storepass '{{ api_password }}' + -keyalg RSA + -keysize 2048 + -validity 109500 + -dname 'CN=localhost, OU=MyCompanyUnit, O=MyCompany, L=MyCity, ST=MyState, C=AU' + -ext san=dns:localhost,ip:127.0.0.1" + args: + chdir: /opt/app/services/{{ teku_service }}/data + changed_when: false + become: yes + + - name: Waiting for the services to start properly + pause: + seconds: 15 + + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: TekuBeaconValidatorService + id: "{{ teku_service }}" + image: "consensys/teku:{{ stereum_static.defaults.versions.teku }}" + ports: + - 0.0.0.0:9001:9001/tcp + - 0.0.0.0:9001:9001/udp + env: + JAVA_OPTS: -Xmx4g + entrypoint: ["/opt/teku/bin/teku"] + command: + - --network=prater + - --data-path=/opt/app/data + - --ee-endpoint=http://stereum-{{ besu_service }}:8551 + - --ee-jwt-secret-file=/engine.jwt + - --validators-builder-registration-default-enabled=true + - --validators-proposer-blinded-blocks-enabled=true + - --validators-proposer-default-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + - --builder-endpoint=http://stereum-{{ mevboost_service }}:18550 + user: "2000" + volumes: + - "/opt/app/services/{{ teku_service }}/data:/opt/app/data" + - "/opt/app/services/{{ besu_service }}/engine.jwt:/engine.jwt" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-teku/create.yml b/controls/roles/manage-service/molecule/mevboost-besu-teku/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-teku/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-teku/destroy.yml b/controls/roles/manage-service/molecule/mevboost-besu-teku/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-teku/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-teku/molecule.yml b/controls/roles/manage-service/molecule/mevboost-besu-teku/molecule.yml new file mode 100644 index 000000000..3986a034b --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-teku/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-besu-teku--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-besu-teku--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-teku/playbook.yml b/controls/roles/manage-service/molecule/mevboost-besu-teku/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-teku/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-teku/prepare.yml b/controls/roles/manage-service/molecule/mevboost-besu-teku/prepare.yml new file mode 100644 index 000000000..d4fed0fda --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-teku/prepare.yml @@ -0,0 +1,50 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install pkgs for Ansible (Ubuntu) + apt: + update_cache: yes + name: + - pip + - python3-pip + - openjdk-8-jre-headless + - expect + state: present + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - name: Install keytool for creating Keystore (CentOS 8) + raw: yum install -y java-1.8.0-openjdk + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - name: Install pip, expect (CentOS 8) + yum: + name: + - python3-pip + - expect + state: latest + become: true + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-besu-teku/verify.yml b/controls/roles/manage-service/molecule/mevboost-besu-teku/verify.yml new file mode 100644 index 000000000..91bca5b7a --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-besu-teku/verify.yml @@ -0,0 +1,26 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-c5fa81c2-1fe2-11ed-8fc8-4bcc92882234" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # teku logs + - name: teku + command: "docker logs stereum-c5a67b18-1fe2-11ed-a441-33f8d671fdfa" + register: teku + until: + - teku.stdout is search("The builder is back online. It will be used for block production") + - teku.stdout is not search("Failed to update eth1 chain head") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/converge.yml b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/converge.yml new file mode 100644 index 000000000..b94a2e7c6 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/converge.yml @@ -0,0 +1,153 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + lighthouse_beacon_service: b7a47794-1fb1-11ed-be39-83068af40ce9 + lighthouse_validator_service: b8568948-1fb1-11ed-9aec-e321a88d2c6e + geth_service: b885924c-1fb1-11ed-a503-733702c8854a + mevboost_service: b8b8eb4c-1fb1-11ed-80cf-578791ba4428 + + tasks: + # geth service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: GethService + id: "{{ geth_service }}" + image: "ethereum/client-go:{{ stereum_static.defaults.versions.geth }}" + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["geth"] + env: {} + command: + - --goerli + - --http + - --datadir=/opt/app/geth + - --http.addr=0.0.0.0 + - --http.vhosts=* + - --http.api=engine,eth,web3,net,debug + - --http.corsdomain=* + - --authrpc.addr=0.0.0.0 + - --authrpc.vhosts=* + - --authrpc.jwtsecret=/engine.jwt + user: "2000" + volumes: + - "/opt/app/services/{{ geth_service }}/data:/opt/app/geth" + - "/opt/app/services/{{ geth_service }}/engine.jwt:/engine.jwt" + + # lh beacon service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: LighthouseBeaconService + id: "{{ lighthouse_beacon_service }}" + image: sigp/lighthouse:{{ stereum_static.defaults.versions.lighthouse }} + env: {} + ports: + - 0.0.0.0:9000:9000/tcp + - 0.0.0.0:9000:9000/udp + command: + - lighthouse + - --network=goerli + - --debug-level=info + - beacon_node + - --datadir=/opt/app/beacon + - --http + - --http-address=0.0.0.0 + - --metrics + - --metrics-address=0.0.0.0 + - --execution-endpoint=http://stereum-{{ geth_service }}:8551 + - --execution-jwt=/engine.jwt + - --builder=http://stereum-{{ mevboost_service }}:18550 + entrypoint: [] + user: "2000" + volumes: + - "/opt/app/services/{{ lighthouse_beacon_service }}/beacon/lighthouse:/opt/app/beacon" + - "/opt/app/services/{{ lighthouse_beacon_service }}/slasher:/opt/app/slasher" + - "/opt/app/services/{{ geth_service }}/engine.jwt:/engine.jwt" + + # lh validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: LighthouseValidatorService + id: "{{ lighthouse_validator_service }}" + image: sigp/lighthouse:{{ stereum_static.defaults.versions.lighthouse }} + env: {} + command: + - lighthouse + - vc + - --debug-level=info + - --network=goerli + - --beacon-nodes=http://stereum-{{ lighthouse_beacon_service }}:5052 + - --datadir=/opt/app/validator + - --metrics + - --metrics-address=0.0.0.0 + - --suggested-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + - --builder-proposals + entrypoint: [] + user: "2000" + volumes: + - "/opt/app/services/{{ lighthouse_validator_service }}/validator:/opt/app/validator" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/create.yml b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/destroy.yml b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/molecule.yml b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/molecule.yml new file mode 100644 index 000000000..7c1927227 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-geth-lighthouse--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-geth-lighthouse--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/playbook.yml b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/prepare.yml b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/prepare.yml new file mode 100644 index 000000000..1473c60cd --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/prepare.yml @@ -0,0 +1,30 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install python for Ansible (Ubuntu) + apt: + update_cache: yes + name: pip + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/verify.yml b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/verify.yml new file mode 100644 index 000000000..3baba09ab --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-lighthouse/verify.yml @@ -0,0 +1,26 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-b8b8eb4c-1fb1-11ed-80cf-578791ba4428" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # lighthouse beacon logs + - name: Lighthouse beacon + command: "docker logs stereum-b7a47794-1fb1-11ed-be39-83068af40ce9" + register: lighthouse_beacon + until: + - lighthouse_beacon.stderr is search("The execution endpoint is connected and configured, however it is not yet synced") + - lighthouse_beacon.stderr is not search("Error connecting to eth1 node endpoint") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-nimbus/converge.yml b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/converge.yml new file mode 100644 index 000000000..5ecaa82f6 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/converge.yml @@ -0,0 +1,135 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + geth_service: ef6ba278-2313-11ed-b0b9-536749fb9bcb + nimbus_service: f0369a0a-2313-11ed-8fe9-4bf0f9c65ff1 + mevboost_service: f0521370-2313-11ed-9a9b-a745c2468767 + + tasks: + # geth service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: GethService + id: "{{ geth_service }}" + image: "ethereum/client-go:{{ stereum_static.defaults.versions.geth }}" + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["geth"] + env: {} + command: + - --goerli + - --http + - --datadir=/opt/app/geth + - --http.addr=0.0.0.0 + - --http.vhosts=* + - --http.api="engine,eth,web3,net,debug" + - --http.corsdomain=* + - --authrpc.addr=0.0.0.0 + - --authrpc.vhosts=* + - --authrpc.jwtsecret=/engine.jwt + user: "2000" + volumes: + - "/opt/app/services/{{ geth_service }}/data:/opt/app/geth" + - "/opt/app/services/{{ geth_service }}/engine.jwt:/engine.jwt" + + # nimbus beacon & validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + - name: Create api-token file + copy: + content: "{{ lookup('password', '/dev/null', seed=inventory_hostname) }}" + dest: /opt/app/services/{{ nimbus_service }}/validator/api-token.txt + force: no + become: yes + - name: Waiting for the services to start properly + pause: + seconds: 15 + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: NimbusBeaconValidatorService + id: "{{ nimbus_service }}" + image: statusim/nimbus-eth2:{{ stereum_static.defaults.versions.nimbus }} + ports: + - 0.0.0.0:9000:9000/tcp + - 0.0.0.0:9000:9000/udp + env: {} + entrypoint: ["/home/user/nimbus-eth2/build/nimbus_beacon_node"] + command: + - --network=goerli + - --data-dir=/opt/app/beacon + - --validators-dir=/opt/app/validators + - --secrets-dir=/opt/app/secrets + - --web3-url=http://stereum-{{ geth_service }}:8551 + - --tcp-port=9000 + - --udp-port=9000 + - --rest + - --rest-address=0.0.0.0 + - --rest-port=5052 + - --jwt-secret=/engine.jwt + - --suggested-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + - --keymanager + - --keymanager-address=0.0.0.0 + - --keymanager-token-file=/opt/app/validators/api-token.txt + - --payload-builder=on + - --payload-builder-url=http://stereum-{{ mevboost_service }}:18550 + user: "2000" + + volumes: + - "/opt/app/services/{{ nimbus_service }}/beacon:/opt/app/beacon" + - "/opt/app/services/{{ nimbus_service }}/validator:/opt/app/validators" + - "/opt/app/services/{{ nimbus_service }}/validator/secrets:/opt/app/secrets" + - "/opt/app/services/{{ geth_service }}/engine.jwt:/engine.jwt" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-nimbus/create.yml b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-nimbus/destroy.yml b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-nimbus/molecule.yml b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/molecule.yml new file mode 100644 index 000000000..8da51dfbf --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-geth-nimbus--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-geth-nimbus--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-nimbus/playbook.yml b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-nimbus/prepare.yml b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/prepare.yml new file mode 100644 index 000000000..1473c60cd --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/prepare.yml @@ -0,0 +1,30 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install python for Ansible (Ubuntu) + apt: + update_cache: yes + name: pip + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-nimbus/verify.yml b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/verify.yml new file mode 100644 index 000000000..a966ef042 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-nimbus/verify.yml @@ -0,0 +1,26 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-f0521370-2313-11ed-9a9b-a745c2468767" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # nimbus logs + - name: Nimbus beacon + command: "docker logs stereum-f0369a0a-2313-11ed-8fe9-4bf0f9c65ff1" + register: nimbus + until: + - nimbus.stdout is search("Established connection to execution layer") + - nimbus.stdout is not search("Payload builder REST client setup failed") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-prysm/converge.yml b/controls/roles/manage-service/molecule/mevboost-geth-prysm/converge.yml new file mode 100644 index 000000000..b4f194bb7 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-prysm/converge.yml @@ -0,0 +1,205 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + prysm_beacon_service: 53bd0386-2321-11ed-8466-a3a699348ce5 + prysm_validator_service: 53e7bb1c-2321-11ed-84a2-bfc2764f627d + geth_service: 540f67c0-2321-11ed-9c66-2fa4bb44067f + mevboost_service: 543898c0-2321-11ed-961b-5fafa9f2267f + + tasks: + # geth service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: GethService + id: "{{ geth_service }}" + image: "ethereum/client-go:{{ stereum_static.defaults.versions.geth }}" + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["geth"] + env: {} + command: + - --goerli + - --http + - --datadir=/opt/app/geth + - --http.addr=0.0.0.0 + - --http.vhosts=* + - --http.api=engine,eth,web3,net,debug + - --http.corsdomain=* + - --authrpc.addr=0.0.0.0 + - --authrpc.vhosts=* + - --authrpc.jwtsecret=/engine.jwt + user: "2000" + volumes: + - "/opt/app/services/{{ geth_service }}/data:/opt/app/geth" + - "/opt/app/services/{{ geth_service }}/engine.jwt:/engine.jwt" + + # prysm beacon service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: PrysmBeaconService + id: "{{ prysm_beacon_service }}" + image: "prysmaticlabs/prysm-beacon-chain:{{ stereum_static.defaults.versions.prysm }}" + ports: + - 0.0.0.0:12000:12000/udp + - 0.0.0.0:13000:13000/tcp + env: {} + entrypoint: [] + command: | + /app/cmd/beacon-chain/beacon-chain + --accept-terms-of-use=true + --datadir=/opt/app/beacon + --goerli=true + --block-batch-limit=512 + --genesis-state=/opt/app/genesis/prysm-prater-genesis.ssz + --rpc-host=0.0.0.0 + --execution-endpoint=http://stereum-{{ geth_service }}:8551 + --jwt-secret=/engine.jwt + --http-mev-relay=http://stereum-{{ mevboost_service }}:18550 + user: "2000" + volumes: + - "/opt/app/services/{{ prysm_beacon_service }}/prysm/beacon:/opt/app/beacon" + - "/opt/app/services/{{ prysm_beacon_service }}/genesis:/opt/app/genesis" + - "/opt/app/services/{{ geth_service }}/engine.jwt:/engine.jwt" + + # prysm validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + - name: Generate wallet's password + command: uuidgen + register: wallet_password + changed_when: false + become: yes + - name: Set wallet password + copy: + content: "{{ wallet_password.stdout }}" + dest: "/opt/app/services/{{ prysm_validator_service }}/data/passwords/wallet-password" + owner: "2000" + group: "2000" + mode: 0700 + become: yes + - name: Prysm - Create wallet for account(s) + command: bash -c "docker exec stereum-{{ prysm_validator_service }} + /app/cmd/validator/validator + wallet create + --wallet-dir=/opt/app/data/wallets + --wallet-password-file=/opt/app/data/passwords/wallet-password + --accept-terms-of-use + --keymanager-kind=direct + --prater" + changed_when: false + become: yes + - name: Prysm - Set wallet permission + ansible.builtin.file: + path: "/opt/app/services/{{ prysm_validator_service }}/data/wallets" + state: directory + recurse: yes + owner: "2000" + group: "2000" + become: yes + - name: Stop beacon & validator service + docker_container: + name: "{{ item }}" + state: stopped + become: yes + with_items: + - "stereum-{{ prysm_beacon_service }}" + - "stereum-{{ prysm_validator_service }}" + - name: Restart beacon & validator service + command: "docker start {{ item }}" + changed_when: false + become: yes + with_items: + - "stereum-{{ prysm_beacon_service }}" + - "stereum-{{ prysm_validator_service }}" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: PrysmValidatorService + id: "{{ prysm_validator_service }}" + image: "prysmaticlabs/prysm-validator:{{ stereum_static.defaults.versions.prysm }}" + env: {} + entrypoint: [] + command: + /app/cmd/validator/validator + --accept-terms-of-use=true + --beacon-rpc-provider="stereum-{{ prysm_beacon_service }}:4000" + --beacon-rpc-gateway-provider="stereum-{{ prysm_beacon_service }}:3500" + --web + --goerli=true + --datadir=/opt/app/data/db + --wallet-dir=/opt/app/data/wallets + --wallet-password-file=/opt/app/data/passwords/wallet-password + --monitoring-host=0.0.0.0 + --grpc-gateway-port=7500 + --grpc-gateway-host=0.0.0.0 + --grpc-gateway-corsdomain="*" + --enable-builder=true + --suggested-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + user: "2000" + volumes: + - "/opt/app/services/{{ prysm_validator_service }}/data/db:/opt/app/data/db" + - "/opt/app/services/{{ prysm_validator_service }}/data/wallets:/opt/app/data/wallets" + - "/opt/app/services/{{ prysm_validator_service }}/data/passwords:/opt/app/data/passwords" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-prysm/create.yml b/controls/roles/manage-service/molecule/mevboost-geth-prysm/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-prysm/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-prysm/destroy.yml b/controls/roles/manage-service/molecule/mevboost-geth-prysm/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-prysm/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-prysm/molecule.yml b/controls/roles/manage-service/molecule/mevboost-geth-prysm/molecule.yml new file mode 100644 index 000000000..65d191105 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-prysm/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-geth-prysm--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-geth-prysm--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-prysm/playbook.yml b/controls/roles/manage-service/molecule/mevboost-geth-prysm/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-prysm/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-prysm/prepare.yml b/controls/roles/manage-service/molecule/mevboost-geth-prysm/prepare.yml new file mode 100644 index 000000000..1473c60cd --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-prysm/prepare.yml @@ -0,0 +1,30 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install python for Ansible (Ubuntu) + apt: + update_cache: yes + name: pip + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-prysm/verify.yml b/controls/roles/manage-service/molecule/mevboost-geth-prysm/verify.yml new file mode 100644 index 000000000..7af624442 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-prysm/verify.yml @@ -0,0 +1,27 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-543898c0-2321-11ed-961b-5fafa9f2267f" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # prysm beacon logs + - name: prysm beacon + command: "docker logs stereum-53bd0386-2321-11ed-8466-a3a699348ce5" + register: prysm_beacon + until: + - prysm_beacon.stderr is search("Builder has been configured") + - prysm_beacon.stderr is search("Connected to new endpoint") + - prysm_beacon.stderr is not search("Could not connect to execution endpoint") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-teku/converge.yml b/controls/roles/manage-service/molecule/mevboost-geth-teku/converge.yml new file mode 100644 index 000000000..3be21591f --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-teku/converge.yml @@ -0,0 +1,153 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + teku_service: d16d704a-1fd4-11ed-a1cb-7743121feb31 + geth_service: d1961d06-1fd4-11ed-9d96-d70f1b6a368a + mevboost_service: d1bfe5d2-1fd4-11ed-94b4-9fb17c6b318c + + tasks: + # geth service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: GethService + id: "{{ geth_service }}" + image: "ethereum/client-go:{{ stereum_static.defaults.versions.geth }}" + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["geth"] + env: {} + command: + - --goerli + - --http + - --datadir=/opt/app/geth + - --http.addr=0.0.0.0 + - --http.vhosts=* + - --http.api=engine,eth,web3,net,debug + - --http.corsdomain=* + - --authrpc.addr=0.0.0.0 + - --authrpc.vhosts=* + - --authrpc.jwtsecret=/engine.jwt + user: "2000" + volumes: + - "/opt/app/services/{{ geth_service }}/data:/opt/app/geth" + - "/opt/app/services/{{ geth_service }}/engine.jwt:/engine.jwt" + + # teku beacon & validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + + - name: "Include manage-service" + include_role: + name: "manage-service" + + - name: Create keystore password + copy: + content: "{{ lookup('password', '/dev/null', seed=inventory_hostname) }}" + dest: /opt/app/services/{{ teku_service }}/data/teku_api_password.txt + force: no + become: yes + + - name: Get teku-api password + slurp: + src: "/opt/app/services/{{ teku_service }}/data/teku_api_password.txt" + register: teku_api_password + become: yes + + - name: Set variable + set_fact: + api_password: "{{ teku_api_password.content | b64decode | trim }}" + + - name: Create keystore file + command: bash -c "keytool -genkeypair + -keystore teku_api_keystore + -storetype PKCS12 + -storepass '{{ api_password }}' + -keyalg RSA + -keysize 2048 + -validity 109500 + -dname 'CN=localhost, OU=MyCompanyUnit, O=MyCompany, L=MyCity, ST=MyState, C=AU' + -ext san=dns:localhost,ip:127.0.0.1" + args: + chdir: /opt/app/services/{{ teku_service }}/data + changed_when: false + become: yes + + - name: Waiting for the services to start properly + pause: + seconds: 15 + + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: TekuBeaconValidatorService + id: "{{ teku_service }}" + image: "consensys/teku:{{ stereum_static.defaults.versions.teku }}" + ports: + - 0.0.0.0:9001:9001/tcp + - 0.0.0.0:9001:9001/udp + env: + JAVA_OPTS: -Xmx4g + entrypoint: ["/opt/teku/bin/teku"] + command: + - --network=prater + - --data-path=/opt/app/data + - --ee-endpoint=http://stereum-{{ geth_service }}:8551 + - --ee-jwt-secret-file=/engine.jwt + - --validators-builder-registration-default-enabled=true + - --validators-proposer-blinded-blocks-enabled=true + - --validators-proposer-default-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + - --builder-endpoint=http://stereum-{{ mevboost_service }}:18550 + user: "2000" + volumes: + - "/opt/app/services/{{ teku_service }}/data:/opt/app/data" + - "/opt/app/services/{{ geth_service }}/engine.jwt:/engine.jwt" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-teku/create.yml b/controls/roles/manage-service/molecule/mevboost-geth-teku/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-teku/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-teku/destroy.yml b/controls/roles/manage-service/molecule/mevboost-geth-teku/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-teku/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-teku/molecule.yml b/controls/roles/manage-service/molecule/mevboost-geth-teku/molecule.yml new file mode 100644 index 000000000..cc80e72dc --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-teku/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-geth-teku--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-geth-teku--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-teku/playbook.yml b/controls/roles/manage-service/molecule/mevboost-geth-teku/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-teku/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-teku/prepare.yml b/controls/roles/manage-service/molecule/mevboost-geth-teku/prepare.yml new file mode 100644 index 000000000..d4fed0fda --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-teku/prepare.yml @@ -0,0 +1,50 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install pkgs for Ansible (Ubuntu) + apt: + update_cache: yes + name: + - pip + - python3-pip + - openjdk-8-jre-headless + - expect + state: present + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - name: Install keytool for creating Keystore (CentOS 8) + raw: yum install -y java-1.8.0-openjdk + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - name: Install pip, expect (CentOS 8) + yum: + name: + - python3-pip + - expect + state: latest + become: true + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-geth-teku/verify.yml b/controls/roles/manage-service/molecule/mevboost-geth-teku/verify.yml new file mode 100644 index 000000000..3b5962490 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-geth-teku/verify.yml @@ -0,0 +1,26 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-d1bfe5d2-1fd4-11ed-94b4-9fb17c6b318c" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # teku logs + - name: teku + command: "docker logs stereum-d16d704a-1fd4-11ed-a1cb-7743121feb31" + register: teku + until: + - teku.stdout is search("The builder is back online. It will be used for block production") + - teku.stdout is not search("Failed to update eth1 chain head") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/converge.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/converge.yml new file mode 100644 index 000000000..2268b0429 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/converge.yml @@ -0,0 +1,150 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + lighthouse_beacon_service: 47cc5bcc-1fc4-11ed-87f9-2fb1a3aea149 + lighthouse_validator_service: 4870dc7e-1fc4-11ed-8ee1-4f4cf8b1962b + nethermind_service: 48a481e6-1fc4-11ed-aa6e-1fbb65c82620 + mevboost_service: 48d6500e-1fc4-11ed-91f4-cb5bd96df843 + + tasks: + # nethermind service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: NethermindService + id: "{{ nethermind_service }}" + image: "nethermind/nethermind:{{ stereum_static.defaults.versions.nethermind }}" + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["./Nethermind.Runner"] + env: {} + command: + - --config=goerli + - --datadir=/opt/app/data + - --Merge.Enabled=true + - --JsonRpc.JwtSecretFile=/engine.jwt + - --JsonRpc.EngineHost=0.0.0.0 + - --JsonRpc.EnginePort=8551 + user: "root" + volumes: + - "/opt/app/services/{{ nethermind_service }}/data:/opt/app/data" + - "/opt/app/services/{{ nethermind_service }}/engine.jwt:/engine.jwt" + + # lh beacon service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: LighthouseBeaconService + id: "{{ lighthouse_beacon_service }}" + image: "sigp/lighthouse:{{ stereum_static.defaults.versions.lighthouse }}" + env: {} + ports: + - 0.0.0.0:9000:9000/tcp + - 0.0.0.0:9000:9000/udp + command: + - lighthouse + - --network=goerli + - --debug-level=info + - beacon_node + - --datadir=/opt/app/beacon + - --http + - --http-address=0.0.0.0 + - --metrics + - --metrics-address=0.0.0.0 + - --suggested-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + - --execution-endpoint=http://stereum-{{ nethermind_service }}:8551 + - --execution-jwt=/engine.jwt + - --builder=http://stereum-{{ mevboost_service }}:18550 + entrypoint: [] + user: "2000" + volumes: + - "/opt/app/services/{{ lighthouse_beacon_service }}/beacon/lighthouse:/opt/app/beacon" + - "/opt/app/services/{{ lighthouse_beacon_service }}/slasher:/opt/app/slasher" + - "/opt/app/services/{{ nethermind_service }}/engine.jwt:/engine.jwt" + + # lh validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: LighthouseValidatorService + id: "{{ lighthouse_validator_service }}" + image: "sigp/lighthouse:{{ stereum_static.defaults.versions.lighthouse }}" + env: {} + command: + - lighthouse + - vc + - --debug-level=info + - --network=goerli + - --beacon-nodes=http://stereum-{{ lighthouse_beacon_service }}:5052 + - --datadir=/opt/app/validator + - --metrics + - --metrics-address=0.0.0.0 + - --suggested-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + - --builder-proposals + entrypoint: [] + user: "2000" + volumes: + - "/opt/app/services/{{ lighthouse_validator_service }}/validator:/opt/app/validator" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: "flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }}" + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/create.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/destroy.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/molecule.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/molecule.yml new file mode 100644 index 000000000..8fc544ed7 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-nethermind-lighthouse--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-nethermind-lighthouse--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/playbook.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/prepare.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/prepare.yml new file mode 100644 index 000000000..1473c60cd --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/prepare.yml @@ -0,0 +1,30 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install python for Ansible (Ubuntu) + apt: + update_cache: yes + name: pip + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/verify.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/verify.yml new file mode 100644 index 000000000..9f60fbe21 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-lighthouse/verify.yml @@ -0,0 +1,26 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-48d6500e-1fc4-11ed-91f4-cb5bd96df843" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # lighthouse beacon logs + - name: Lighthouse beacon + command: "docker logs stereum-47cc5bcc-1fc4-11ed-87f9-2fb1a3aea149" + register: lighthouse_beacon + until: + - lighthouse_beacon.stderr is search("The execution endpoint is connected and configured, however it is not yet synced") + - lighthouse_beacon.stderr is not search("Error connecting to eth1 node endpoint") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/converge.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/converge.yml new file mode 100644 index 000000000..c259290a9 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/converge.yml @@ -0,0 +1,133 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + nethermind_service: 2863e820-231d-11ed-98f1-cf0e0c718b2b + nimbus_service: 28bf6a92-231d-11ed-ae9e-c75fab7a77b6 + mevboost_service: 28eefbc2-231d-11ed-80f7-0b2dcd8ce871 + + tasks: + # nethermind service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: NethermindService + id: "{{ nethermind_service }}" + image: "nethermind/nethermind:{{ stereum_static.defaults.versions.nethermind }}" + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["./Nethermind.Runner"] + env: {} + command: + - --config=goerli + - --datadir=/opt/app/data + - --Merge.Enabled=true + - --JsonRpc.JwtSecretFile=/engine.jwt + - --JsonRpc.EngineHost=0.0.0.0 + - --JsonRpc.EnginePort=8551 + user: "root" + volumes: + - "/opt/app/services/{{ nethermind_service }}/data:/opt/app/data" + - "/opt/app/services/{{ nethermind_service }}/engine.jwt:/engine.jwt" + + # nimbus beacon & validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + - name: Create api-token file + copy: + content: "{{ lookup('password', '/dev/null', seed=inventory_hostname) }}" + dest: /opt/app/services/{{ nimbus_service }}/validator/api-token.txt + force: no + become: yes + - name: Waiting for the services to start properly + pause: + seconds: 15 + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: NimbusBeaconValidatorService + id: "{{ nimbus_service }}" + image: statusim/nimbus-eth2:{{ stereum_static.defaults.versions.nimbus }} + ports: + - 0.0.0.0:9000:9000/tcp + - 0.0.0.0:9000:9000/udp + env: {} + entrypoint: ["/home/user/nimbus-eth2/build/nimbus_beacon_node"] + command: + - --network=goerli + - --data-dir=/opt/app/beacon + - --validators-dir=/opt/app/validators + - --secrets-dir=/opt/app/secrets + - --web3-url=http://stereum-{{ nethermind_service }}:8551 + - --tcp-port=9000 + - --udp-port=9000 + - --metrics + - --metrics-port=8008 + - --metrics-address=0.0.0.0 + - --rest + - --rest-address=0.0.0.0 + - --rest-port=5052 + - --jwt-secret=/engine.jwt + - --suggested-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + - --keymanager + - --keymanager-address=0.0.0.0 + - --keymanager-token-file=/opt/app/validators/api-token.txt + - --payload-builder=on + - --payload-builder-url=http://stereum-{{ mevboost_service }}:18550 + user: "2000" + volumes: + - "/opt/app/services/{{ nimbus_service }}/beacon:/opt/app/beacon" + - "/opt/app/services/{{ nimbus_service }}/validator:/opt/app/validators" + - "/opt/app/services/{{ nimbus_service }}/validator/secrets:/opt/app/secrets" + - "/opt/app/services/{{ nethermind_service }}/engine.jwt:/engine.jwt" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/create.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/destroy.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/molecule.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/molecule.yml new file mode 100644 index 000000000..6540e69f9 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-nethermind-nimbus--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-nethermind-nimbus--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/playbook.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/prepare.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/prepare.yml new file mode 100644 index 000000000..1473c60cd --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/prepare.yml @@ -0,0 +1,30 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install python for Ansible (Ubuntu) + apt: + update_cache: yes + name: pip + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/verify.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/verify.yml new file mode 100644 index 000000000..343ba7f4c --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-nimbus/verify.yml @@ -0,0 +1,25 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-28eefbc2-231d-11ed-80f7-0b2dcd8ce871" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # nimbus logs + - name: Nimbus beacon + command: "docker logs stereum-28bf6a92-231d-11ed-ae9e-c75fab7a77b6" + register: nimbus + until: + - nimbus.stdout is search("Established connection to execution layer") + - nimbus.stdout is not search("Payload builder REST client setup failed") + retries: 60 + delay: 10 +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/converge.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/converge.yml new file mode 100644 index 000000000..5bd063a5d --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/converge.yml @@ -0,0 +1,201 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + prysm_beacon_service: a8645d22-2343-11ed-8d14-bfe4b8cce4a9 + prysm_validator_service: a88b48ba-2343-11ed-abca-b3dc95ed08f4 + nethermind_service: a8ac2814-2343-11ed-bb84-777f6a806038 + mevboost_service: a8d611ec-2343-11ed-958c-6764986b8e2a + + tasks: + # nethermind service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: NethermindService + id: "{{ nethermind_service }}" + image: "nethermind/nethermind:{{ stereum_static.defaults.versions.nethermind }}" + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["./Nethermind.Runner"] + env: {} + command: + - --config=goerli + - --datadir=/opt/app/data + - --Merge.Enabled=true + - --JsonRpc.JwtSecretFile=/engine.jwt + - --JsonRpc.EngineHost=0.0.0.0 + - --JsonRpc.EnginePort=8551 + user: "root" + volumes: + - "/opt/app/services/{{ nethermind_service }}/data:/opt/app/data" + - "/opt/app/services/{{ nethermind_service }}/engine.jwt:/engine.jwt" + + # prysm beacon service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: PrysmBeaconService + id: "{{ prysm_beacon_service }}" + image: "prysmaticlabs/prysm-beacon-chain:{{ stereum_static.defaults.versions.prysm }}" + ports: + - 0.0.0.0:12000:12000/udp + - 0.0.0.0:13000:13000/tcp + env: {} + entrypoint: [] + command: | + /app/cmd/beacon-chain/beacon-chain + --accept-terms-of-use=true + --datadir=/opt/app/beacon + --goerli=true + --block-batch-limit=512 + --genesis-state=/opt/app/genesis/prysm-prater-genesis.ssz + --rpc-host=0.0.0.0 + --execution-endpoint=http://stereum-{{ nethermind_service }}:8551 + --jwt-secret=/engine.jwt + --http-mev-relay=http://stereum-{{ mevboost_service }}:18550 + user: "2000" + volumes: + - "/opt/app/services/{{ prysm_beacon_service }}/prysm/beacon:/opt/app/beacon" + - "/opt/app/services/{{ prysm_beacon_service }}/genesis:/opt/app/genesis" + - "/opt/app/services/{{ nethermind_service }}/engine.jwt:/engine.jwt" + + # prysm validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + - name: Generate wallet's password + command: uuidgen + register: wallet_password + changed_when: false + become: yes + - name: Set wallet password + copy: + content: "{{ wallet_password.stdout }}" + dest: "/opt/app/services/{{ prysm_validator_service }}/data/passwords/wallet-password" + owner: "2000" + group: "2000" + mode: 0700 + become: yes + - name: Prysm - Create wallet for account(s) + command: bash -c "docker exec stereum-{{ prysm_validator_service }} + /app/cmd/validator/validator + wallet create + --wallet-dir=/opt/app/data/wallets + --wallet-password-file=/opt/app/data/passwords/wallet-password + --accept-terms-of-use + --keymanager-kind=direct + --prater" + changed_when: false + become: yes + - name: Prysm - Set wallet permission + ansible.builtin.file: + path: "/opt/app/services/{{ prysm_validator_service }}/data/wallets" + state: directory + recurse: yes + owner: "2000" + group: "2000" + become: yes + - name: Stop beacon & validator service + docker_container: + name: "{{ item }}" + state: stopped + become: yes + with_items: + - "stereum-{{ prysm_beacon_service }}" + - "stereum-{{ prysm_validator_service }}" + - name: Restart beacon & validator service + command: "docker start {{ item }}" + changed_when: false + become: yes + with_items: + - "stereum-{{ prysm_beacon_service }}" + - "stereum-{{ prysm_validator_service }}" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: PrysmValidatorService + id: "{{ prysm_validator_service }}" + image: "prysmaticlabs/prysm-validator:{{ stereum_static.defaults.versions.prysm }}" + env: {} + entrypoint: [] + command: + /app/cmd/validator/validator + --accept-terms-of-use=true + --beacon-rpc-provider="stereum-{{ prysm_beacon_service }}:4000" + --beacon-rpc-gateway-provider="stereum-{{ prysm_beacon_service }}:3500" + --web + --goerli=true + --datadir=/opt/app/data/db + --wallet-dir=/opt/app/data/wallets + --wallet-password-file=/opt/app/data/passwords/wallet-password + --monitoring-host=0.0.0.0 + --grpc-gateway-port=7500 + --grpc-gateway-host=0.0.0.0 + --grpc-gateway-corsdomain="*" + --enable-builder=true + --suggested-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + user: "2000" + volumes: + - "/opt/app/services/{{ prysm_validator_service }}/data/db:/opt/app/data/db" + - "/opt/app/services/{{ prysm_validator_service }}/data/wallets:/opt/app/data/wallets" + - "/opt/app/services/{{ prysm_validator_service }}/data/passwords:/opt/app/data/passwords" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/create.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/destroy.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/molecule.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/molecule.yml new file mode 100644 index 000000000..0260a8480 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-nethermind-prysm--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-nethermind-prysm--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/playbook.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/prepare.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/prepare.yml new file mode 100644 index 000000000..1473c60cd --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/prepare.yml @@ -0,0 +1,30 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install python for Ansible (Ubuntu) + apt: + update_cache: yes + name: pip + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/verify.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/verify.yml new file mode 100644 index 000000000..e3d5b0c26 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-prysm/verify.yml @@ -0,0 +1,27 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-a8d611ec-2343-11ed-958c-6764986b8e2a" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # prysm beacon logs + - name: prysm beacon + command: "docker logs stereum-a8645d22-2343-11ed-8d14-bfe4b8cce4a9" + register: prysm_beacon + until: + - prysm_beacon.stderr is search("Builder has been configured") + - prysm_beacon.stderr is search("Connected to new endpoint") + - prysm_beacon.stderr is not search("Could not connect to execution endpoint") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-teku/converge.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/converge.yml new file mode 100644 index 000000000..4c4eeefda --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/converge.yml @@ -0,0 +1,149 @@ +--- +- name: Converge + hosts: all + vars_files: + - ../../../../defaults/stereum_defaults.yaml + vars: + teku_service: 0c851526-1fe8-11ed-a4bc-a304c2a39bd6 + nethermind_service: 0cb6869c-1fe8-11ed-a431-a752cb022fb5 + mevboost_service: 0ce6b4ac-1fe8-11ed-b9c2-bb5736e3d599 + + tasks: + # nethermind service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: NethermindService + id: "{{ nethermind_service }}" + image: "nethermind/nethermind:{{ stereum_static.defaults.versions.nethermind }}" + ports: + - 0.0.0.0:30303:30303/tcp + - 0.0.0.0:30303:30303/udp + entrypoint: ["./Nethermind.Runner"] + env: {} + command: + - --config=goerli + - --datadir=/opt/app/data + - --Merge.Enabled=true + - --JsonRpc.JwtSecretFile=/engine.jwt + - --JsonRpc.EngineHost=0.0.0.0 + - --JsonRpc.EnginePort=8551 + user: "root" + volumes: + - "/opt/app/services/{{ nethermind_service }}/data:/opt/app/data" + - "/opt/app/services/{{ nethermind_service }}/engine.jwt:/engine.jwt" + + # teku beacon & validator service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + + - name: "Include manage-service" + include_role: + name: "manage-service" + + - name: Create keystore password + copy: + content: "{{ lookup('password', '/dev/null', seed=inventory_hostname) }}" + dest: /opt/app/services/{{ teku_service }}/data/teku_api_password.txt + force: no + become: yes + + - name: Get teku-api password + slurp: + src: "/opt/app/services/{{ teku_service }}/data/teku_api_password.txt" + register: teku_api_password + become: yes + + - name: Set variable + set_fact: + api_password: "{{ teku_api_password.content | b64decode | trim }}" + + - name: Create keystore file + command: bash -c "keytool -genkeypair + -keystore teku_api_keystore + -storetype PKCS12 + -storepass '{{ api_password }}' + -keyalg RSA + -keysize 2048 + -validity 109500 + -dname 'CN=localhost, OU=MyCompanyUnit, O=MyCompany, L=MyCity, ST=MyState, C=AU' + -ext san=dns:localhost,ip:127.0.0.1" + args: + chdir: /opt/app/services/{{ teku_service }}/data + changed_when: false + become: yes + + - name: Waiting for the services to start properly + pause: + seconds: 15 + + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: TekuBeaconValidatorService + id: "{{ teku_service }}" + image: "consensys/teku:{{ stereum_static.defaults.versions.teku }}" + ports: + - 0.0.0.0:9001:9001/tcp + - 0.0.0.0:9001:9001/udp + env: + JAVA_OPTS: -Xmx4g + entrypoint: ["/opt/teku/bin/teku"] + command: + - --network=prater + - --data-path=/opt/app/data + - --ee-endpoint=http://stereum-{{ nethermind_service }}:8551 + - --ee-jwt-secret-file=/engine.jwt + - --validators-builder-registration-default-enabled=true + - --validators-proposer-blinded-blocks-enabled=true + - --validators-proposer-default-fee-recipient=0x5dC29815e46dfb5EAb5C57606f8e2A5FbBdb454e + - --builder-endpoint=http://stereum-{{ mevboost_service }}:18550 + user: "2000" + volumes: + - "/opt/app/services/{{ teku_service }}/data:/opt/app/data" + - "/opt/app/services/{{ nethermind_service }}/engine.jwt:/engine.jwt" + + # mevboost service + - block: + - set_fact: + stereum: "{{ stereum_static | combine(stereum_args, recursive=True) }}" + - name: "Include manage-service" + include_role: + name: "manage-service" + vars: + stereum_args: + manage_service: + save: true + state: started + configuration: + service: MevBoostService + id: "{{ mevboost_service }}" + image: flashbots/mev-boost:{{ stereum_static.defaults.versions.mevboost }} + env: {} + ports: [] + command: [] + entrypoint: + - /app/mev-boost + - -addr + - 0.0.0.0:18550 + - -goerli + - -relay-check + - -relays + - "{{ stereum_static.defaults.relay.goerli }}" + user: "2000" + volumes: [] + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-teku/create.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/create.yml new file mode 100644 index 000000000..9475eea78 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/create.yml @@ -0,0 +1,95 @@ +--- +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + vars: + ssh_port: 22 + ssh_user: root + ssh_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + tasks: + - name: Create SSH key + user: + name: "{{ lookup('env', 'USER') }}" + generate_ssh_key: true + ssh_key_file: "{{ ssh_path }}" + force: true + register: generated_ssh_key + + - name: Register the SSH key name + set_fact: + ssh_key_name: "molecule-generated-{{ 12345 | random | to_uuid }}" + + - name: Register SSH key for test instance(s) + hcloud_ssh_key: + name: "{{ ssh_key_name }}" + public_key: "{{ generated_ssh_key.ssh_public_key }}" + state: present + + - name: Create molecule instance(s) + hcloud_server: + name: "{{ item.name }}" + server_type: "{{ item.server_type }}" + ssh_keys: + - "{{ ssh_key_name }}" + volumes: "{{ item.volumes | default(omit) }}" + image: "{{ item.image }}" + datacenter: "{{ item.datacenter | default(omit) }}" + user_data: "{{ item.user_data | default(omit) }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: present + register: server + with_items: "{{ molecule_yml.platforms }}" + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Attach Server to Subnetwork(s) + hcloud_server_network: + network: "{{ item.network.name }}" + server: "{{ item.name }}" + ip: "{{ item.network.ip }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: "present" + loop: "{{ molecule_yml.platforms }}" + when: molecule_yml.platforms is search('network') + + - name: Populate instance config dict + set_fact: + instance_conf_dict: { + 'instance': "{{ item.hcloud_server.name }}", + 'ssh_key_name': "{{ ssh_key_name }}", + 'address': "{{ item.hcloud_server.ipv4_address }}", + 'user': "{{ ssh_user }}", + 'port': "{{ ssh_port }}", + 'identity_file': "{{ ssh_path }}", } + with_items: "{{ hetzner_jobs.results }}" + register: instance_config_dict + when: server.changed | bool + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + when: server.changed | bool + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool + + - name: Wait for SSH + wait_for: + port: "{{ ssh_port }}" + host: "{{ item.address }}" + search_regex: SSH + delay: 10 + with_items: "{{ lookup('file', molecule_instance_config) | from_yaml }}" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-teku/destroy.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/destroy.yml new file mode 100644 index 000000000..0168cc5a0 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/destroy.yml @@ -0,0 +1,55 @@ +--- +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Populate the instance config + block: + - name: Populate instance config from file + set_fact: + instance_conf: "{{ lookup('file', molecule_instance_config) | from_yaml }}" + skip_instances: false + rescue: + - name: Populate instance config when file missing + set_fact: + instance_conf: {} + skip_instances: true + + - name: Destroy molecule instance(s) + hcloud_server: + name: "{{ item.instance }}" + api_token: "{{ lookup('env', 'HCLOUD_TOKEN') }}" + state: absent + register: server + with_items: "{{ instance_conf }}" + when: not skip_instances + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + async_status: + jid: "{{ item.ansible_job_id }}" + register: hetzner_jobs + until: hetzner_jobs.finished + retries: 300 + with_items: "{{ server.results }}" + + - name: Remove registered SSH key + hcloud_ssh_key: + name: "{{ instance_conf[0].ssh_key_name }}" + state: absent + when: + - not skip_instances + - instance_conf | length # must contain at least one instance + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config + copy: + content: "{{ instance_conf | to_yaml }}" + dest: "{{ molecule_instance_config }}" + when: server.changed | bool \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-teku/molecule.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/molecule.yml new file mode 100644 index 000000000..e1dc70634 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/molecule.yml @@ -0,0 +1,33 @@ +--- +dependency: + name: galaxy +driver: + name: hetznercloud +platforms: + - name: "manage-service--mevboost-nethermind-teku--ubuntu-22.04" + hostname: ubuntu + server_type: cpx31 + image: ubuntu-22.04 +# - name: "manage-service--mevboost-nethermind-teku--centos-stream-8" +# hostname: "centos" +# server_type: cpx31 +# image: centos-stream-8 +provisioner: + name: ansible + config_options: + ssh_connection: + ssh_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s +lint: | + set -e + yamllint . + ansible-lint . +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + #- idempotence + - lint + - verify + - destroy \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-teku/playbook.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/playbook.yml new file mode 100644 index 000000000..10b4b7e40 --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: "Include manage-service" + include_role: + name: "manage-service" \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-teku/prepare.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/prepare.yml new file mode 100644 index 000000000..d4fed0fda --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/prepare.yml @@ -0,0 +1,50 @@ +--- +- name: Prepare + hosts: all + roles: + - role: '../' + vars_files: + - ../../../../defaults/stereum_defaults.yaml + + tasks: + - name: Install pkgs for Ansible (Ubuntu) + apt: + update_cache: yes + name: + - pip + - python3-pip + - openjdk-8-jre-headless + - expect + state: present + become: true + changed_when: false + when: ansible_distribution == "Ubuntu" + + - name: Install python for Ansible (CentOS 8) + raw: yum install -y python38 tar && yum remove -y python36 + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - name: Install keytool for creating Keystore (CentOS 8) + raw: yum install -y java-1.8.0-openjdk + become: true + changed_when: false + when: ansible_distribution == "CentOS" + + - name: Install pip, expect (CentOS 8) + yum: + name: + - python3-pip + - expect + state: latest + become: true + when: ansible_distribution == "CentOS" + + - include_role: + name: "setup" + + - include_role: + name: "configure-firewall" + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/mevboost-nethermind-teku/verify.yml b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/verify.yml new file mode 100644 index 000000000..5f0ede91b --- /dev/null +++ b/controls/roles/manage-service/molecule/mevboost-nethermind-teku/verify.yml @@ -0,0 +1,26 @@ +--- +- name: Verify + hosts: all + gather_facts: false + tasks: + # mev boost logs + - name: MEV-boost + command: "docker logs stereum-0ce6b4ac-1fe8-11ed-b9c2-bb5736e3d599" + register: mev_boost + until: + - mev_boost.stdout is search("listening on 0.0.0.0:18550") + - mev_boost.stdout is search("using 1 relays") + - mev_boost.stdout is not search("Invalid relay URL") + retries: 60 + delay: 10 + # teku logs + - name: teku + command: "docker logs stereum-0c851526-1fe8-11ed-a4bc-a304c2a39bd6" + register: teku + until: + - teku.stdout is search("The builder is back online. It will be used for block production") + - teku.stdout is not search("Failed to update eth1 chain head") + retries: 60 + delay: 10 + +# EOF \ No newline at end of file diff --git a/controls/roles/manage-service/molecule/nethermind-prysm/converge.yml b/controls/roles/manage-service/molecule/nethermind-prysm/converge.yml index 77ad89409..49173911b 100644 --- a/controls/roles/manage-service/molecule/nethermind-prysm/converge.yml +++ b/controls/roles/manage-service/molecule/nethermind-prysm/converge.yml @@ -74,7 +74,7 @@ --datadir=/opt/app/beacon --prater=true --genesis-state=/opt/app/genesis/prysm-prater-genesis.ssz - --http-web3provider=http://stereum-{{ nethermind_service }}:8551 + --execution-endpoint=http://stereum-{{ nethermind_service }}:8551 --jwt-secret=/engine.jwt user: "2000" volumes: diff --git a/controls/roles/manage-service/molecule/ssv-prysm/prepare.yml b/controls/roles/manage-service/molecule/ssv-prysm/prepare.yml index 238b69411..26fcb6634 100644 --- a/controls/roles/manage-service/molecule/ssv-prysm/prepare.yml +++ b/controls/roles/manage-service/molecule/ssv-prysm/prepare.yml @@ -134,7 +134,7 @@ --monitoring-host=0.0.0.0 --grpc-gateway-host=0.0.0.0 --grpc-gateway-port=3500 - --http-web3provider=http://stereum-{{ geth_service }}:8551 + --execution-endpoint=http://stereum-{{ geth_service }}:8551 --prater=true --fallback-web3provider=[] --block-batch-limit=512