├── .github └── workflows │ ├── ansible-galaxy.yml │ ├── linter.yml │ ├── test-blockbook.yml │ ├── test-ethereum.yml │ ├── test-nano.yml │ ├── test-near.yml │ ├── test-osmosis.yml │ ├── test-polygon.yml │ └── test-tron.yml ├── .gitignore ├── .yamllint.yml ├── LICENSE ├── README.md ├── ansible.cfg ├── docs ├── blockbook_role.md ├── cosmos_role.md ├── ethereum_role.md ├── nano_role.md ├── near_role.md ├── polygon_role.md ├── publishing_to_galaxy.md └── tron_role.md ├── galaxy.yml ├── meta └── runtime.yml ├── molecule ├── blockbook │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ ├── molecule.yml │ ├── prepare.yml │ └── tests │ │ ├── conftest.py │ │ ├── pytest.ini │ │ └── test_chain.py ├── cosmos │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ ├── molecule.yml │ ├── prepare.yml │ └── tests │ │ ├── conftest.py │ │ ├── pytest.ini │ │ └── test_chain.py ├── ethereum │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ ├── molecule.yml │ ├── prepare.yml │ └── tests │ │ └── test_chain.py ├── nano │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ ├── molecule.yml │ ├── prepare.yml │ └── tests │ │ └── test_chain.py ├── near │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ ├── molecule.yml │ ├── prepare.yml │ └── tests │ │ └── test_chain.py ├── polygon │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ ├── molecule.yml │ ├── prepare.yml │ └── tests │ │ └── test_chain.py └── tron │ ├── config │ └── main_net_config.conf │ ├── converge.yml │ ├── create.yml │ ├── destroy.yml │ ├── molecule.yml │ ├── prepare.yml │ └── tests │ └── test_chain.py ├── plugins ├── README.md └── filter │ └── filters.py ├── requirements.txt ├── requirements.yml └── roles ├── blockbook ├── README.md ├── defaults │ └── main.yml ├── meta │ └── main.yml ├── tasks │ └── main.yml └── templates │ └── environ.json.j2 ├── cosmos ├── README.md ├── defaults │ └── main.yml ├── files │ └── checksum.sh ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── tasks │ ├── config.yml │ ├── config_osmosis.yml │ ├── init.yml │ ├── install.yml │ ├── load_vars.yml │ ├── main.yml │ └── sync.yml ├── templates │ └── service.j2 └── vars │ ├── osmosis.yml │ └── terra.yml ├── ethereum ├── README.md ├── defaults │ └── main.yml ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── tasks │ ├── config.yml │ ├── install.yml │ └── main.yml ├── templates │ ├── config.toml.j2 │ └── service.j2 └── tests │ ├── inventory │ └── test.yml ├── nano ├── README.md ├── defaults │ └── main.yml ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── tasks │ ├── install.yml │ ├── main.yml │ ├── nano_node.yml │ ├── nano_rpc_cache.yml │ └── nano_work_server.yml └── templates │ ├── docker-compose.yml.j2 │ ├── nano-node │ ├── config-node.toml.j2 │ └── config-rpc.toml.j2 │ ├── nano-rpc-cache │ └── config.toml.j2 │ ├── nano_rpc_cache_service.j2 │ └── nano_work_server_service.j2 ├── near ├── README.md ├── defaults │ └── main.yml ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── tasks │ ├── init.yml │ ├── install.yml │ ├── main.yml │ └── sync.yml └── templates │ └── service.j2 ├── polygon ├── README.md ├── defaults │ └── main.yml ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── tasks │ ├── config.yml │ ├── init.yml │ ├── install.yml │ ├── main.yml │ └── sync.yml └── templates │ ├── bor-config.toml.j2 │ ├── bor.service.j2 │ ├── heimdalld-rest-server.service.j2 │ └── heimdalld.service.j2 └── tron ├── README.md ├── defaults └── main.yml ├── files └── main_net_config.conf ├── handlers └── main.yml ├── meta └── main.yml ├── tasks ├── config.yml ├── install.yml ├── main.yml └── sync.yml └── templates └── service.j2 /.github/workflows/ansible-galaxy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ansible Galaxy 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - galaxy.yml 10 | 11 | jobs: 12 | ansible-galaxy: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | 21 | - name: Setup Python 22 | uses: actions/setup-python@v2 23 | 24 | - name: Install Ansible package 25 | run: pip install ansible 26 | 27 | - name: Build Ansible Galaxy collection 28 | id: build 29 | run: | 30 | VERSION=$(ansible-galaxy collection build | sed 's/.*-\(.*\)\.tar\.gz/\1/') 31 | echo "::set-output name=collection-version::$VERSION" 32 | 33 | - name: Publish Ansible Galaxy collection 34 | run: ansible-galaxy collection publish --api-key ${{ secrets.GALAXY_API_KEY }} trustwallet-blockchain-*.tar.gz 35 | 36 | - name: Push tag 37 | id: tag_version 38 | uses: mathieudutour/github-tag-action@v6.0 39 | with: 40 | custom_tag: ${{ steps.build.outputs.collection-version }} 41 | github_token: ${{ secrets.GITHUB_TOKEN }} 42 | 43 | - name: Create a GitHub release 44 | uses: ncipollo/release-action@v1 45 | with: 46 | tag: ${{ steps.tag_version.outputs.new_tag }} 47 | name: ${{ steps.tag_version.outputs.new_tag }} 48 | body: ${{ steps.tag_version.outputs.changelog }} 49 | -------------------------------------------------------------------------------- /.github/workflows/linter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Linter 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | ansible-lint: 11 | name: "ansible-lint" 12 | runs-on: "ubuntu-latest" 13 | steps: 14 | - uses: "actions/checkout@v2" 15 | - uses: "actions/setup-python@v2" 16 | - run: "pip install ansible ansible-lint boto3" 17 | - run: "ansible-lint ." 18 | yamllint: 19 | name: "yamllint" 20 | runs-on: "ubuntu-latest" 21 | steps: 22 | - uses: "actions/checkout@v2" 23 | - uses: "actions/setup-python@v2" 24 | - run: "pip install yamllint" 25 | - run: "yamllint -s ." 26 | -------------------------------------------------------------------------------- /.github/workflows/test-blockbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test Blockbook role (firo) 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | paths: 9 | - roles/blockbook/** 10 | - molecule/blockbook/** 11 | 12 | permissions: 13 | id-token: write 14 | contents: read 15 | 16 | env: 17 | MOLECULE_SCENARIO: blockbook 18 | 19 | jobs: 20 | ansible-galaxy: 21 | name: "ansible-galaxy" 22 | runs-on: "ubuntu-latest" 23 | steps: 24 | - uses: actions/checkout@v2 25 | 26 | - name: Confiugre AWS credentials 27 | uses: aws-actions/configure-aws-credentials@v1 28 | with: 29 | role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} 30 | role-skip-session-tagging: true 31 | aws-region: ${{ secrets.AWS_REGION }} 32 | 33 | - uses: actions/setup-python@v2 34 | - uses: actions/cache@v2 35 | id: cache-pip 36 | with: 37 | path: ~/.cache/pip 38 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} 39 | restore-keys: | 40 | ${{ runner.os }}-pip- 41 | - run: pip install -r requirements.txt 42 | if: steps.cache.outputs.cache-hit != 'true' 43 | 44 | # role has dependency during testing with Molecule which is not resolved 45 | # for some reason --role-path specified during molecule run is not used during prepare play 46 | # maybe it's a molecule-ec2 package issue 47 | - run: ansible-galaxy role install -vr requirements.yml 48 | 49 | - name: Test role with Molecule 50 | if: github.repository_owner == 'trustwallet' 51 | run: molecule -v test -s $MOLECULE_SCENARIO 52 | env: 53 | AWS_REGION: ${{ secrets.AWS_REGION }} 54 | MOLECULE_VPC_SUBNET_ID: ${{ secrets.MOLECULE_VPC_SUBNET_ID }} 55 | MOLECULE_IMAGE_DEBIAN: ${{ secrets.MOLECULE_IMAGE_DEBIAN }} 56 | BLOCKBOOK_CHAIN_NAME: firo 57 | -------------------------------------------------------------------------------- /.github/workflows/test-ethereum.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test Ethereum role 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | paths: 9 | - roles/ethereum/** 10 | - molecule/ethereum/** 11 | 12 | permissions: 13 | id-token: write 14 | contents: read 15 | 16 | env: 17 | MOLECULE_SCENARIO: ethereum 18 | 19 | jobs: 20 | ansible-galaxy: 21 | name: "ansible-galaxy" 22 | runs-on: "ubuntu-latest" 23 | steps: 24 | - uses: actions/checkout@v2 25 | 26 | - name: Confiugre AWS credentials 27 | uses: aws-actions/configure-aws-credentials@v1 28 | with: 29 | role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} 30 | role-skip-session-tagging: true 31 | aws-region: ${{ secrets.AWS_REGION }} 32 | 33 | - uses: actions/setup-python@v2 34 | - uses: actions/cache@v2 35 | id: cache-pip 36 | with: 37 | path: ~/.cache/pip 38 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} 39 | restore-keys: | 40 | ${{ runner.os }}-pip- 41 | - run: pip install -r requirements.txt 42 | if: steps.cache.outputs.cache-hit != 'true' 43 | 44 | - name: Test role with Molecule 45 | if: github.repository_owner == 'trustwallet' 46 | run: molecule -v test -s $MOLECULE_SCENARIO 47 | env: 48 | AWS_REGION: ${{ secrets.AWS_REGION }} 49 | MOLECULE_VPC_SUBNET_ID: ${{ secrets.MOLECULE_VPC_SUBNET_ID }} 50 | MOLECULE_IMAGE: ${{ secrets.MOLECULE_IMAGE }} 51 | -------------------------------------------------------------------------------- /.github/workflows/test-nano.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test Nano role 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | paths: 9 | - roles/nano/** 10 | - molecule/nano/** 11 | 12 | permissions: 13 | id-token: write 14 | contents: read 15 | 16 | env: 17 | MOLECULE_SCENARIO: nano 18 | 19 | jobs: 20 | ansible-galaxy: 21 | name: "ansible-galaxy" 22 | runs-on: "ubuntu-latest" 23 | steps: 24 | - uses: actions/checkout@v2 25 | 26 | - name: Confiugre AWS credentials 27 | uses: aws-actions/configure-aws-credentials@v1 28 | with: 29 | role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} 30 | role-skip-session-tagging: true 31 | aws-region: ${{ secrets.AWS_REGION }} 32 | 33 | - uses: actions/setup-python@v2 34 | - uses: actions/cache@v2 35 | id: cache-pip 36 | with: 37 | path: ~/.cache/pip 38 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} 39 | restore-keys: | 40 | ${{ runner.os }}-pip- 41 | - run: pip install -r requirements.txt 42 | if: steps.cache.outputs.cache-hit != 'true' 43 | 44 | # role has dependency during testing with Molecule which is not resolved 45 | # for some reason --role-path specified during molecule run is not used during prepare play 46 | # maybe it's a molecule-ec2 package issue 47 | - run: ansible-galaxy role install -vr requirements.yml 48 | 49 | - name: Test role with Molecule 50 | if: github.repository_owner == 'trustwallet' 51 | run: molecule -v test -s $MOLECULE_SCENARIO 52 | env: 53 | AWS_REGION: ${{ secrets.AWS_REGION }} 54 | MOLECULE_VPC_SUBNET_ID: ${{ secrets.MOLECULE_VPC_SUBNET_ID }} 55 | MOLECULE_IMAGE: ${{ secrets.MOLECULE_IMAGE }} 56 | -------------------------------------------------------------------------------- /.github/workflows/test-near.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test NEAR role 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | paths: 9 | - roles/near/** 10 | - molecule/near/** 11 | 12 | permissions: 13 | id-token: write 14 | contents: read 15 | 16 | env: 17 | MOLECULE_SCENARIO: near 18 | 19 | jobs: 20 | ansible-galaxy: 21 | name: "ansible-galaxy" 22 | runs-on: "ubuntu-latest" 23 | steps: 24 | - uses: actions/checkout@v2 25 | 26 | - name: Confiugre AWS credentials 27 | uses: aws-actions/configure-aws-credentials@v1 28 | with: 29 | role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} 30 | role-skip-session-tagging: true 31 | aws-region: ${{ secrets.AWS_REGION }} 32 | 33 | - uses: actions/setup-python@v2 34 | - uses: actions/cache@v2 35 | id: cache-pip 36 | with: 37 | path: ~/.cache/pip 38 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} 39 | restore-keys: | 40 | ${{ runner.os }}-pip- 41 | - run: pip install -r requirements.txt 42 | if: steps.cache.outputs.cache-hit != 'true' 43 | 44 | - uses: actions/cache@v2 45 | id: cache-neard-bin 46 | with: 47 | path: /usr/local/bin/neard 48 | key: ${{ runner.os }}-neard 49 | 50 | # role has dependency during testing with Molecule which is not resolved 51 | # for some reason --role-path specified during molecule run is not used during prepare play 52 | # maybe it's a molecule-ec2 package issue 53 | - run: ansible-galaxy role install -vr requirements.yml 54 | 55 | - name: Test role with Molecule 56 | if: github.repository_owner == 'trustwallet' 57 | run: molecule -v test -s $MOLECULE_SCENARIO 58 | env: 59 | AWS_REGION: ${{ secrets.AWS_REGION }} 60 | MOLECULE_VPC_SUBNET_ID: ${{ secrets.MOLECULE_VPC_SUBNET_ID }} 61 | MOLECULE_IMAGE: ${{ secrets.MOLECULE_IMAGE }} 62 | -------------------------------------------------------------------------------- /.github/workflows/test-osmosis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test Cosmos role (osmosis) 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | paths: 9 | - roles/cosmos/** 10 | - molecule/cosmos/** 11 | 12 | permissions: 13 | id-token: write 14 | contents: read 15 | 16 | env: 17 | MOLECULE_SCENARIO: cosmos 18 | 19 | jobs: 20 | ansible-galaxy: 21 | name: "ansible-galaxy" 22 | runs-on: "ubuntu-latest" 23 | steps: 24 | - uses: actions/checkout@v2 25 | 26 | - name: Confiugre AWS credentials 27 | uses: aws-actions/configure-aws-credentials@v1 28 | with: 29 | role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} 30 | role-skip-session-tagging: true 31 | aws-region: ${{ secrets.AWS_REGION }} 32 | 33 | - uses: actions/setup-python@v2 34 | - uses: actions/cache@v2 35 | id: cache-pip 36 | with: 37 | path: ~/.cache/pip 38 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} 39 | restore-keys: | 40 | ${{ runner.os }}-pip- 41 | - run: pip install -r requirements.txt 42 | if: steps.cache.outputs.cache-hit != 'true' 43 | 44 | # cosmos role is using the collection plugin 45 | - run: ansible-galaxy collection build 46 | - run: find trustwallet-blockchain-* | xargs ansible-galaxy collection install 47 | 48 | # role has dependency during testing with Molecule which is not resolved 49 | # for some reason --role-path specified during molecule run is not used during prepare play 50 | # maybe it's a molecule-ec2 package issue 51 | - run: ansible-galaxy role install -vr requirements.yml 52 | 53 | - name: Test role with Molecule 54 | if: github.repository_owner == 'trustwallet' 55 | run: molecule -v test -s $MOLECULE_SCENARIO 56 | env: 57 | AWS_REGION: ${{ secrets.AWS_REGION }} 58 | MOLECULE_VPC_SUBNET_ID: ${{ secrets.MOLECULE_VPC_SUBNET_ID }} 59 | MOLECULE_IMAGE: ${{ secrets.MOLECULE_IMAGE }} 60 | COSMOS_CHAIN_NAME: osmosis 61 | -------------------------------------------------------------------------------- /.github/workflows/test-polygon.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test Polygon role 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | paths: 9 | - roles/polygon/** 10 | - molecule/polygon/** 11 | 12 | permissions: 13 | id-token: write 14 | contents: read 15 | 16 | env: 17 | MOLECULE_SCENARIO: polygon 18 | 19 | jobs: 20 | ansible-galaxy: 21 | name: "ansible-galaxy" 22 | runs-on: "ubuntu-latest" 23 | steps: 24 | - uses: actions/checkout@v2 25 | 26 | - name: Confiugre AWS credentials 27 | uses: aws-actions/configure-aws-credentials@v1 28 | with: 29 | role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} 30 | role-skip-session-tagging: true 31 | aws-region: ${{ secrets.AWS_REGION }} 32 | 33 | - uses: actions/setup-python@v2 34 | - uses: actions/cache@v2 35 | id: cache-pip 36 | with: 37 | path: ~/.cache/pip 38 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} 39 | restore-keys: | 40 | ${{ runner.os }}-pip- 41 | - run: pip install -r requirements.txt 42 | if: steps.cache.outputs.cache-hit != 'true' 43 | 44 | # role has dependency during testing with Molecule which is not resolved 45 | # for some reason --role-path specified during molecule run is not used during prepare play 46 | # maybe it's a molecule-ec2 package issue 47 | - run: ansible-galaxy role install -vr requirements.yml 48 | 49 | - name: Test role with Molecule 50 | if: github.repository_owner == 'trustwallet' 51 | run: molecule -v test -s $MOLECULE_SCENARIO 52 | env: 53 | AWS_REGION: ${{ secrets.AWS_REGION }} 54 | MOLECULE_VPC_SUBNET_ID: ${{ secrets.MOLECULE_VPC_SUBNET_ID }} 55 | MOLECULE_IMAGE: ${{ secrets.MOLECULE_IMAGE }} 56 | -------------------------------------------------------------------------------- /.github/workflows/test-tron.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test TRON role 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | paths: 9 | - roles/tron/** 10 | - molecule/tron/** 11 | 12 | permissions: 13 | id-token: write 14 | contents: read 15 | 16 | env: 17 | MOLECULE_SCENARIO: tron 18 | 19 | jobs: 20 | ansible-galaxy: 21 | name: "ansible-galaxy" 22 | runs-on: "ubuntu-latest" 23 | steps: 24 | - uses: actions/checkout@v2 25 | 26 | - name: Confiugre AWS credentials 27 | uses: aws-actions/configure-aws-credentials@v1 28 | with: 29 | role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} 30 | role-skip-session-tagging: true 31 | aws-region: ${{ secrets.AWS_REGION }} 32 | 33 | - uses: actions/setup-python@v2 34 | - uses: actions/cache@v2 35 | id: cache-pip 36 | with: 37 | path: ~/.cache/pip 38 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} 39 | restore-keys: | 40 | ${{ runner.os }}-pip- 41 | - run: pip install -r requirements.txt 42 | if: steps.cache.outputs.cache-hit != 'true' 43 | 44 | # role has dependency during testing with Molecule which is not resolved 45 | # for some reason --role-path specified during molecule run is not used during prepare play 46 | # maybe it's a molecule-ec2 package issue 47 | - run: ansible-galaxy role install -vr requirements.yml 48 | 49 | - name: Test role with Molecule 50 | if: github.repository_owner == 'trustwallet' 51 | run: molecule -v test -s $MOLECULE_SCENARIO 52 | env: 53 | AWS_REGION: ${{ secrets.AWS_REGION }} 54 | MOLECULE_VPC_SUBNET_ID: ${{ secrets.MOLECULE_VPC_SUBNET_ID }} 55 | MOLECULE_IMAGE: ${{ secrets.MOLECULE_IMAGE }} 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | tests/output 3 | __pycache__ 4 | 5 | # Visual Studio Code 6 | .vscode 7 | 8 | # Python Environments 9 | .env 10 | .venv 11 | env/ 12 | venv/ 13 | ENV/ 14 | env.bak/ 15 | venv.bak/ 16 | 17 | # ansible-galaxy 18 | *.tar.gz 19 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | ignore: | 4 | **/lib/ 5 | **/.ansible/ 6 | rules: 7 | braces: 8 | min-spaces-inside: 0 9 | max-spaces-inside: 0 10 | brackets: 11 | forbid: "non-empty" 12 | colons: 13 | max-spaces-after: 1 14 | max-spaces-before: 0 15 | commas: 16 | max-spaces-after: 1 17 | max-spaces-before: 0 18 | min-spaces-after: 1 19 | comments: 20 | ignore-shebangs: false 21 | min-spaces-from-content: 1 22 | require-starting-space: true 23 | comments-indentation: "enable" 24 | document-end: 25 | present: false 26 | document-start: 27 | present: true 28 | empty-lines: 29 | max: 1 30 | max-end: 0 31 | max-start: 0 32 | empty-values: 33 | forbid-in-block-mappings: true 34 | forbid-in-flow-mappings: true 35 | hyphens: 36 | max-spaces-after: 1 37 | indentation: 38 | check-multi-line-strings: false 39 | indent-sequences: true 40 | spaces: 2 41 | key-duplicates: "enable" 42 | key-ordering: "disable" 43 | line-length: "disable" 44 | new-line-at-end-of-file: "enable" 45 | new-lines: 46 | type: "unix" 47 | octal-values: 48 | forbid-explicit-octal: true 49 | forbid-implicit-octal: true 50 | quoted-strings: 51 | quote-type: "double" 52 | required: false 53 | trailing-spaces: "enable" 54 | truthy: 55 | allowed-values: 56 | - "true" 57 | - "false" 58 | check-keys: false 59 | 60 | yaml-files: 61 | - "*.yml" 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2022 Trust Wallet 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible Collection - trustwallet.blockchain 2 | 3 | :shield: Trust Wallet is a most trusted & secure non-custodial crypto wallet. 4 | 5 | The Trust Wallet's Ansible Galaxy collection of roles to configure blockchain nodes. 6 | 7 | NOTE: These roles set up non-validator nodes. Please check the requirements and 8 | an additional configuration at the chain's documentation if the validator or other node 9 | type is required. 10 | 11 | List of the collection roles: 12 | 13 | * [trustwallet.blockchain.blockbook](https://github.com/trustwallet/ansible-collection-blockchain/tree/main/roles/blockbook) - Blockbook supported blockchains 14 | * [trustwallet.blockchain.cosmos](https://github.com/trustwallet/ansible-collection-blockchain/tree/main/roles/cosmos) - All Cosmos-SDK based blockchains 15 | * [trustwallet.blockchain.ethereum](https://github.com/trustwallet/ansible-collection-blockchain/tree/main/roles/ethereum) - Ethereum blockchain with Geth 16 | * [trustwallet.blockchain.nano](https://github.com/trustwallet/ansible-collection-blockchain/tree/main/roles/nano) - Nano blockchain 17 | * [trustwallet.blockchain.near](https://github.com/trustwallet/ansible-collection-blockchain/tree/main/roles/near) - NEAR blockchain 18 | * [trustwallet.blockchain.polygon](https://github.com/trustwallet/ansible-collection-blockchain/tree/main/roles/polygon) - Polygon blockchain 19 | * [trustwallet.blockchain.tron](https://github.com/trustwallet/ansible-collection-blockchain/tree/main/roles/tron) - TRON blockchain 20 | * _...more are coming_ 21 | 22 | ## Ansible Collection Usage 23 | 24 | Install the collection using the following command: 25 | 26 | ```shell 27 | ansible-galaxy collection install trustwallet.blockchain 28 | ``` 29 | 30 | Example setting up Ethereum Full Node with the collection role: 31 | 32 | ```yaml 33 | # playbook.yml 34 | --- 35 | - hosts: "all" 36 | gather_facts: true 37 | become: true 38 | 39 | pre_tasks: 40 | - name: "Install apt packages" 41 | ansible.builtin.apt: 42 | update_cache: true 43 | cache_valid_time: 86400 44 | pkg: python3 45 | 46 | roles: 47 | - role: trustwallet.blockchain.ethereum 48 | data_dir: /mnt/data 49 | geth_config: 50 | Eth: 51 | SyncMode: full 52 | ``` 53 | 54 | ## Concept 55 | 56 | Ansible Roles consists of few phases (not every role contains all phase though): 57 | 58 | - load variables 59 | - install binaries 60 | - init blockchain node 61 | - set configuration 62 | - sync chain data from public backup 63 | 64 | ## Contributing 65 | 66 | The best way to submit feedback and report bugs is to [open a GitHub issue](https://github.com/trustwallet/ansible-collection-blockchains/issues). 67 | 68 | ## Development 69 | 70 | ### Molecule Testing Framework 71 | 72 | Development/integration of the roles are configured with Molecule and EC2 driver. 73 | 74 | It's recommended to develop inside [Virtual environment](https://virtualenv.pypa.io/en/latest/) 75 | 76 | ```shell 77 | virtualenv -p python3 venv 78 | source venv/bin/activate 79 | pip install -r requirements.txt 80 | ansible-galaxy install -r requirements.yml 81 | ``` 82 | 83 | Provide AWS credentials to allow Molecule provision ephemeral EC2 instance. 84 | 85 | ```shell 86 | export AWS_PROFILE= 87 | # or 88 | export AWS_ACCESS_KEY_ID= 89 | export AWS_SECRET_KEY= 90 | ``` 91 | 92 | Also provide the target region, image AMI and VPC subnet identifier 93 | 94 | ```shell 95 | export AWS_REGION=us-east-1 96 | export MOLECULE_VPC_SUBNET_ID=subnet-... 97 | export MOLECULE_IMAGE=ami-... # ubuntu 98 | export MOLECULE_IMAGE_DEBIAN=ami-... # debian for blockbook 99 | ``` 100 | 101 | For each role there is a `/molecule/` directory with configuration files. 102 | 103 | Execute full integration scenario which includes steps to install all dependencies, create EC2 instance, execute role, test, cleanup and finally destroy EC2 instance run the following command (e.g. `ethereum` role). 104 | 105 | ```shell 106 | molecule test -s ethereum 107 | ``` 108 | 109 | Or launch instance and execute a role 110 | 111 | ```shell 112 | molecule converge -s ethereum 113 | ``` 114 | 115 | And verify 116 | 117 | ```shell 118 | molecule verify -s ethereum 119 | ``` 120 | 121 | Jump to the node 122 | 123 | ```shell 124 | molecule login -s ethereum 125 | ``` 126 | 127 | Finally, cleanup and destroy 128 | 129 | ```shell 130 | molecule destroy -s ethereum 131 | ``` 132 | 133 | ### Build Ansible Galaxy Locally 134 | 135 | To build the local version of the Ansible Galaxy collection: 136 | 137 | ```sh 138 | ansible-galaxy collection build --force 139 | ``` 140 | 141 | The `trustwallet-blockchain-x.x.x.tar.gz` file will appear at the root of the project. 142 | 143 | It can be installed for local testing by executing the following command: 144 | 145 | ```sh 146 | ansible-galaxy collection install trustwallet-blockchain-0.x.x.tar.gz --force 147 | ``` 148 | 149 | ## References 150 | 151 | * [Trust Wallet](https://trustwallet.com) crypto wallet project page 152 | * [Trust Wallet Collection](https://galaxy.ansible.com/trustwallet/blockchain) Ansible Galaxy page 153 | * [Molecule](https://molecule.readthedocs.io/en/latest/index.html) Ansible roles testing framework 154 | * [Testinfra](https://testinfra.readthedocs.io/en/latest/) unit tests in Python to test actual state of the server configured by Ansible/Molecule 155 | 156 | ## License 157 | 158 | Ansible Collection `trustwallet.blockchain` is available under the [MIT](LICENSE) license. 159 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | pipelining = True 3 | callbacks_enabled = ansible.posix.profile_tasks 4 | -------------------------------------------------------------------------------- /docs/blockbook_role.md: -------------------------------------------------------------------------------- 1 | # Blockbook Role 2 | 3 | Ansible role to manage [Blockbook](https://github.com/trezor/blockbook) supported blockchains. 4 | 5 | ## Requirements 6 | 7 | Officially supported platform is Debian Linux and AMD64 architecture. 8 | 9 | ### Docker CE 10 | 11 | This role doesn't attempt to install the Docker CE, but assumes it is available. 12 | 13 | Tip: Docker can be installed with Ansible Galaxy [geerlingguy.docker](https://galaxy.ansible.com/geerlingguy/docker) role or similar. 14 | 15 | ## Role Variables 16 | 17 | The role has default variables (see `defaults/main.yml`) which can be adjusted. 18 | 19 | ## Example Playbook 20 | 21 | ```yaml 22 | --- 23 | - hosts: "all" 24 | gather_facts: true 25 | become: true 26 | remote_user: admin 27 | 28 | roles: 29 | - role: geerlingguy.docker 30 | 31 | - role: trustwallet.blockchain.blockbook 32 | chain_name: firo 33 | data_dir: /mnt/data/ # example of custom data_dir, default is /root 34 | ``` 35 | 36 | ## Development 37 | 38 | To aid in the development and testing of the Ansible role, we are 39 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 40 | 41 | The role to test the Blockbook supported blockchains expects the `BLOCKBOOK_CHAIN_NAME` parameter 42 | to be set as environment variable. 43 | 44 | ```shell 45 | BLOCKBOOK_CHAIN_NAME=firo molecule -v test -s blockbook 46 | ``` 47 | 48 | ## References 49 | 50 | * [Trust Wallet](https://trustwallet.com) 51 | * [Blockbook](https://github.com/trezor/blockbook) 52 | 53 | ## License 54 | 55 | MIT 56 | -------------------------------------------------------------------------------- /docs/cosmos_role.md: -------------------------------------------------------------------------------- 1 | # Cosmos Role 2 | 3 | Ansible role to manage Cosmos-SDK based blockchain nodes. 4 | 5 | ## Requirements 6 | 7 | The Cosmos-SDK based chains are distributed for may operating systems. 8 | Check the project's GitHub repository to see if it releases binary for the 9 | operating system of your choice. But this role only targeting the Linux/Unix 10 | based OS. 11 | 12 | ### Golang 13 | 14 | This role doesn't attempt to install the golang, but assumes it is available 15 | at the default location `/usr/local/go/bin`. The default location can be overridden 16 | with the `golang_bin_dir` variable when executing the role. 17 | 18 | Tip: Golang can be installed with Ansible Galaxy [gantsign.golang](https://galaxy.ansible.com/gantsign/golang) role or similar. 19 | 20 | ### Role Variables 21 | 22 | The role loads the variables in the following order: 23 | 24 | :arrow_down: Role defaults `defaults/main.yml` 25 | :arrow_down: Role chain variables `vars/.yml` (optional) 26 | :arrow_down: Playbook `vars` 27 | :arrow_down: Role `vars` 28 | :arrow_down: Role parameters 29 | 30 | As noted, the Cosmos-SDK based chain might have optional variables file 31 | (e.g. `vars/osmosis.yml`) to override certain config variables. 32 | 33 | The general rule here, if it makes sense to have the parameter configurable, 34 | then submit a pull request to add the flag to `defaults/main.yml` or 35 | `vars/.yml` for specific chain overrides, and modify `tasks/config.yml` or 36 | `tasks/config_.yml` to apply the introduced variable. 37 | 38 | ### SystemD ExecStart 39 | 40 | The execution command is the following: 41 | 42 | ```shell 43 | {{ bin_dir }}/{{ chain_bin }} start --home {{ data_dir }} {{ extra_run_args }} 44 | ``` 45 | 46 | Some Cosmos-SDK based chains accept additional parameters as command-line flags. 47 | These flags can be specified via `extra_run_args` variable. F.e. `terrad` suggests 48 | `--x-crisis-skip-assert-invariants` flag to start syncing the node since the last upgrade 49 | until it is at the current height (we have already set this variable in `vars/terra.yml`). 50 | 51 | ## Example Playbook 52 | 53 | ```yaml 54 | --- 55 | - hosts: "all" 56 | gather_facts: true 57 | become: true 58 | 59 | roles: 60 | - role: gantsign.golang 61 | golang_version: "1.18.1" 62 | golang_install_dir: /usr/local/go 63 | 64 | - role: trustwallet.blockchain.cosmos 65 | chain_name: osmosis 66 | data_dir: /mnt/data/.osmosisd # example of custom data_dir, default is /home/cosmos/.osmosisd 67 | quicksync_mode: default # options are pruned, default, archive and none (sync from scratch) 68 | 69 | ``` 70 | 71 | ## Popular Questions 72 | 73 | ### What ports does Cosmos-SDK based chain node use? 74 | 75 | By default chain's binary uses the following ports: 76 | 77 | * `26656` - p2p networking port to connect to the tendermint network 78 | On a validator this port needs to be exposed to sentry nodes 79 | On a sentry node this port needs to be exposed to the open internet 80 | 81 | * `26657` - Tendermint RPC port 82 | This should be shielded from the open internet 83 | 84 | * `26658` - Out of process ABCI app 85 | This should be shielded from the open internet 86 | 87 | * `26660` - Prometheus stats server 88 | Stats about the gaiad process 89 | Needs to be enabled in the config file . 90 | This should be shielded from the open internet 91 | 92 | * `1317` - Light Client Daemon 93 | For automated management of anything you can do with the CLI 94 | This should be shielded from the open internet, unless public sentry node is planned. 95 | 96 | ## Development 97 | 98 | To aid in the development and testing of the Ansible role, we are 99 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 100 | 101 | The role to test the Cosmos-SDK based chains, expects the `COSMOS_CHAIN_NAME` parameter 102 | to be set as environment variable. 103 | 104 | ```shell 105 | COSMOS_CHAIN_NAME=osmosis molecule -v test -s cosmos 106 | ``` 107 | 108 | ## References 109 | 110 | * [Trust Wallet](https://trustwallet.com) 111 | * [Cosmos SDK](https://v1.cosmos.network/sdk) 112 | * [Cosmos-SDK based chains registry](https://github.com/cosmos/chain-registry/) 113 | 114 | ## License 115 | 116 | MIT 117 | -------------------------------------------------------------------------------- /docs/ethereum_role.md: -------------------------------------------------------------------------------- 1 | # Ethereum Role 2 | 3 | Ansible role to manage Ethereum blockchain node with Geth. 4 | 5 | ## Requirements 6 | 7 | The `geth` can be launched on many operating systems, but this role at the moment only targeting Linux/Unix based operating systems. 8 | 9 | ## Role Variables 10 | 11 | The role has default geth configuration (see `defaults/main.yml`) combined with the `geth_config` override passed as a role parameter. 12 | 13 | Geth variables could also be configured by specifying `extra_run_args` variable 14 | when executing the role. 15 | 16 | The execution command is the following: 17 | 18 | ```shell 19 | {{ chain_bin }} --config {{ data_dir }}/config.toml {{ extra_run_args }} 20 | ``` 21 | 22 | ## Example Playbook 23 | 24 | ```yaml 25 | - hosts: "all" 26 | gather_facts: true 27 | become: true 28 | 29 | roles: 30 | - role: trustwallet.blockchain.ethereum 31 | data_dir: /mnt/data # example of custom data dir, default is /home/geth/.ethereum 32 | geth_config: 33 | Eth: 34 | SyncMode: full 35 | ``` 36 | 37 | ## Development 38 | 39 | To aid in the development and testing of the Ansible role, we are 40 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 41 | 42 | ```shell 43 | molecule -v test -s ethereum 44 | ``` 45 | 46 | ## References 47 | 48 | * [Trust Wallet](https://trustwallet.com) 49 | * [Go Ethereum (geth)](https://geth.ethereum.org/) 50 | 51 | ## License 52 | 53 | MIT 54 | -------------------------------------------------------------------------------- /docs/nano_role.md: -------------------------------------------------------------------------------- 1 | # Nano Role 2 | 3 | Ansible role to manage Nano blockchain node. 4 | 5 | ## Role Variables 6 | 7 | The role has default variables (see `defaults/main.yml`) which can be adjusted. 8 | 9 | ## Example Playbook 10 | 11 | ```yaml 12 | - hosts: "all" 13 | gather_facts: true 14 | 15 | roles: 16 | - role: geerlingguy.docker # for nano_node 17 | become: true 18 | - role: gantsign.golang # for nano_rpc_cache 19 | golang_version: "1.18.1" 20 | golang_install_dir: /usr/local/go 21 | - role: nvidia.nvidia_driver # for nano-work-server 22 | become: true 23 | 24 | - role: trustwallet.blockchain.nano 25 | data_dir: /mnt/data/nano # exampe of non-default data directory, default is /home/nano/.nano 26 | nano_node: true # default, could be omitted 27 | nano_work_server: true # default, could be omitted 28 | nano_work_server_peers: # default, could be omitted 29 | - http://localhost:7776 30 | nano_work_server: true # default, Work Generation needs a machine with GPU attached 31 | ``` 32 | 33 | Nano documentation suggests to split RPC node and Work Generation to separate machines, but single setup machine still valid. 34 | 35 | Alternative setup with two machines: 36 | 37 | ```yaml 38 | - hosts: "nano_node" 39 | gather_facts: true 40 | 41 | roles: 42 | - role: geerlingguy.docker # for nano_node 43 | become: true 44 | - role: gantsign.golang # for nano_rpc_cache 45 | golang_version: "1.18.1" 46 | golang_install_dir: /usr/local/go 47 | 48 | - role: trustwallet.blockchain.nano 49 | data_dir: /mnt/data/.nano # exampe of non-default data directory, default is /home/nano/.nano 50 | nano_node: true 51 | nano_work_server: false 52 | nano_work_server_peers: # note: only a single worker server peer is supported atm 53 | - https://:7776 54 | 55 | - hosts: "nano_work_server" 56 | gather_facts: true 57 | 58 | roles: 59 | - role: nvidia.nvidia_driver # for nano-work-server 60 | become: true 61 | 62 | - role: trustwallet.blockchain.nano 63 | nano_node: false 64 | nano_work_server: true 65 | ``` 66 | 67 | ## Popular Questions 68 | 69 | ### Hardware Recommendations 70 | 71 | Non-voting and Representative Nodes: 72 | 73 | * 8GB RAM 74 | * Quad-Core CPU 75 | * 250 Mbps bandwidth (4TB or more of available monthly bandwidth) 76 | * SSD-based hard drive with 400GB+ of free space 77 | 78 | For nodes being used with services requiring regular or high volume sending and receiving of transactions, 79 | special considerations must be made for handling Proof-of-Work generation activities. 80 | See [Work Generation](https://docs.nano.org/integration-guides/work-generation/) guidance. 81 | 82 | NOTE: ARM based platforms is not yet supported for Nano Work Server. 83 | 84 | NOTE: This role doesn't install any GPU drivers, should be installed separately (examples shows the NVIDIA driver installation). 85 | 86 | ### Port Configuration 87 | 88 | * `7075` - Nano P2P port for the node to participate on the network, should be open to the world `0.0.0.0/0`. 89 | 90 | * `7076` - Nano RPC access port, should only be available to those you wish to have control of the node, since option to `enable_control` is set to on. 91 | 92 | * `7376` - [Nano RPC Cache](https://github.com/catenocrypt/nano-work-cache) is an additional caching layer (not official), which simplifies interaction for 93 | light wallets which need to perform work generation on the server side, 94 | provides cache for some actions, and blocks potentially risky actions from being invoked outside of the local peers, should be open to the world `0.0.0.0/0`. 95 | 96 | * `7176` - Nano Worker Server port, so Nano PRC can request work generation. 97 | 98 | ## Troubleshooting 99 | 100 | ### nano-work-server – Unable to get platform id list after 10 seconds of waiting. 101 | 102 |  🔰 **Ensure drivers are installed correctly** 103 | 104 | On Linux you can check with the `sudo lshw -C display` command. 105 | 106 |
107 | Example output 108 | 109 | ```sh 110 | $ sudo lshw -C display 111 | *-display:0 UNCLAIMED 112 | description: VGA compatible controller 113 | product: GD 5446 114 | vendor: Cirrus Logic 115 | physical id: 2 116 | bus info: pci@0000:00:02.0 117 | version: 00 118 | width: 32 bits 119 | clock: 33MHz 120 | capabilities: vga_controller 121 | configuration: latency=0 122 | resources: memory:e8000000-e9ffffff memory:ee080000-ee080fff memory:c0000-dffff 123 | *-display:1 124 | description: VGA compatible controller 125 | product: GK104GL [GRID K520] 126 | vendor: NVIDIA Corporation 127 | physical id: 3 128 | bus info: pci@0000:00:03.0 129 | version: a1 130 | width: 64 bits 131 | clock: 33MHz 132 | capabilities: pm msi pciexpress vga_controller bus_master cap_list rom 133 | configuration: driver=nvidia latency=248 134 | resources: irq:114 memory:ec000000-ecffffff memory:e0000000-e7ffffff memory:ea000000-ebffffff ioport:c100(size=128) memory:ee000000-ee07ffff 135 | ``` 136 |
137 | 138 | Should be no `UNCLAIMED` next to the target GPU device (in the example NVIDIA GRID K520 card has drivers installed correctly). 139 | 140 | 🔰 **Which type of driver do I need?** 141 | 142 | It really depends on the system specification and goes far beyond this Ansible role. But in our tests and working environment we are running on AWS `g5.2xlarge` GPU Accelerated instances with NVIDIA GRID K520 GPUs. The working driver for this graphics card can be installed with Ansible role: 143 | 144 | ```yml 145 | roles: 146 | - role: nvidia.nvidia_driver 147 | nvidia_driver_branch: 470 # not the latest, since latest 510 branch not working 148 | ``` 149 | 150 | 🔰 **Driver is fine, but doesn't work** 151 | 152 | As it says in [nano-work-server](https://github.com/nanocurrency/nano-work-server) project, if the OpenCL library cannot be found in the PATH, it may be necessary to link against explicitly. It can be done by passing the `opencl_lib_path` variable. 153 | 154 | ```yml 155 | roles: 156 | - role: trustwallet.blockchain.nano 157 | opencl_lib_path: "/path/to/opencl.lib" 158 | ``` 159 | 160 | 🔰 **ARM based platform? Not yet supported for Nano Work Server** 161 | 162 | There is an open [issue](https://github.com/nanocurrency/nano-work-server/issues/33) at the project GitHub. 163 | 164 | ## Development 165 | 166 | To aid in the development and testing of the Ansible role, we are 167 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 168 | 169 | ```shell 170 | molecule -v test -s nano 171 | ``` 172 | 173 | ## References 174 | 175 | * [Trust Wallet](https://trustwallet.com) 176 | * [Nano GitHub](https://github.com/nanocurrency/nano-node) 177 | * [Nano Documentation](https://docs.nano.org/running-a-node/overview/) 178 | * [Nano RPC commands](https://docs.nano.org/commands/rpc-protocol/) 179 | * [Nano Troubleshooting](https://docs.nano.org/running-a-node/troubleshooting/) 180 | * [Nano RPC Cache](https://github.com/catenocrypt/nano-work-cache) 181 | * [Nano Work Server](https://github.com/nanocurrency/nano-work-server) 182 | 183 | ## License 184 | 185 | MIT 186 | -------------------------------------------------------------------------------- /docs/near_role.md: -------------------------------------------------------------------------------- 1 | # NEAR Role 2 | 3 | Ansible role to manage NEAR blockchain node. 4 | 5 | The role installs Rust to compile NEAR binary. The compilation takes very long, be patient. 6 | 7 | ## Role Variables 8 | 9 | The role has default variables (see `defaults/main.yml`) which can be adjusted. 10 | 11 | ## Example Playbook 12 | 13 | ```yaml 14 | - hosts: "all" 15 | gather_facts: true 16 | become: true 17 | 18 | roles: 19 | - role: trustwallet.blockchain.near 20 | data_dir: /mnt/data/.near # exampe of non-default data directory, default is /home/near/.near 21 | quicksync_mode: rpc # options are rpc, archive and none (sync from scratch) 22 | ``` 23 | 24 | ## Development 25 | 26 | To aid in the development and testing of the Ansible role, we are 27 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 28 | 29 | ```shell 30 | molecule -v test -s near 31 | ``` 32 | 33 | ## References 34 | 35 | * [Trust Wallet](https://trustwallet.com) 36 | * [NEAR GitHub](https://github.com/near/nearcore) 37 | * [NEAR Documentation](https://docs.near.org/docs/develop/node/intro/what-is-a-node) 38 | 39 | ## License 40 | 41 | MIT 42 | -------------------------------------------------------------------------------- /docs/polygon_role.md: -------------------------------------------------------------------------------- 1 | # Polygon Role 2 | 3 | Ansible role to manage Polygon blockchain node. 4 | 5 | The role is based on the original [Polygon Ansible playbook](https://github.com/maticnetwork/node-ansible), 6 | but contains automatic snapshot sync. 7 | 8 | NOTE: The role currently sets up only the Full RPC node. 9 | To set up the `sentry/validator` node feel free 10 | to follow the original instruction or submit a Pull Request. 11 | 12 | ## Requirements 13 | 14 | ### Golang 15 | 16 | This role doesn't attempt to install the golang, but assumes it is available 17 | at the default location `/usr/local/go/bin`. The default location can be overridden 18 | with the `golang_bin_dir` variable when executing the role. 19 | 20 | Tip: Golang can be installed with Ansible Galaxy [gantsign.golang](https://galaxy.ansible.com/gantsign/golang) role or similar. 21 | 22 | 23 | ## Role Variables 24 | 25 | The role has default variables (see `defaults/main.yml`) which can be adjusted. 26 | 27 | ## Example Playbook 28 | 29 | ```yaml 30 | - hosts: "all" 31 | gather_facts: true 32 | become: true 33 | 34 | roles: 35 | - role: gantsign.golang 36 | golang_version: "1.18.1" 37 | golang_install_dir: /usr/local/go 38 | 39 | - role: trustwallet.blockchain.polygon 40 | data_dir: /mnt/data # exampe of non-default data directory, default is /home/polygon 41 | quicksync_mode: full # options are archive, full, pruned and none (sync bor from scratch) 42 | bor_config: 43 | HTTPModules: 44 | - eth 45 | - net 46 | - web3 47 | - txpool 48 | - bor 49 | ``` 50 | 51 | ## Popular Questions 52 | 53 | ### Port Configuration 54 | 55 | The machine must have the following ports open: 56 | 57 | * `26656` - Heimdall P2P port to connect the node to other nodes, should be open to the world `0.0.0.0/0`. 58 | 59 | * `26657` - Heimdall RPC port, should not be exposed to the world. 60 | 61 | * `1317` - Heimdall REST API port, will be used by Bor. 62 | 63 | * `30303` - Bor P2P port to connect the node to other nodes,should be open to the world `0.0.0.0/0`. 64 | 65 | * `8545` - Bor RPC port (used for HTTP API and websocket). 66 | 67 | * `22` - For the validator to be able to ssh open to the specific subnet where Validator is configured. 68 | 69 | 70 | ### What are the sizes of Polygon Chains Snapshots? 71 | 72 | * Mainnet Archive Bor snapshot ~10TiB 73 | * Mainnet FullNode Bor snapshot ~870GiB 74 | * Mainnet Pruned Bor snapshot ~700GiB 75 | * Mainnet Heimdall snapshot ~130GiB 76 | 77 | ## Development 78 | 79 | To aid in the development and testing of the Ansible role, we are 80 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 81 | 82 | ```shell 83 | molecule -v test -s polygon 84 | ``` 85 | 86 | ## References 87 | 88 | * [Trust Wallet](https://trustwallet.com) 89 | * [Original Polygon Full Node Deployment with Ansible](https://docs.polygon.technology/docs/develop/network-details/full-node-deployment/) 90 | * [Polygon Chains Snapshots](https://snapshots.matic.today/) 91 | * [Snapshot Instructions for Heimdall and Bor](https://forum.matic.network/t/snapshot-instructions-for-heimdall-and-bor/2278) 92 | * Alternatively, [Run Polygon node with Docker](https://chasewright.com/how-to-run-a-polygon-matic-mainnet-node/) 93 | 94 | ## License 95 | 96 | MIT 97 | -------------------------------------------------------------------------------- /docs/publishing_to_galaxy.md: -------------------------------------------------------------------------------- 1 | # Publishing New Versions 2 | 3 | 1. Install `antsibull-changelog` Ansible changelog tool 4 | ```shell 5 | pip install antsibull-changelog 6 | ``` 7 | 2. Ensure all relevant PRs have provided changelog fragments, then generate a changelog entries for new version: 8 | ```shell 9 | antsibull-changelog release --version X.Y.Z --date YYYY-MM-DD 10 | ``` 11 | 3. Update `galaxy.yml` file and `requirements.yml` example in `README.md` with the new `version` for the collection. 12 | 4. Tag the version in Git and push to GitHub: 13 | ```shell 14 | git tag -a X.Y.Z 15 | git push origin X.Y.Z 16 | ``` 17 | 18 | Additional manual steps are required when automatic publish to Ansible Galaxy 19 | is not enabled in the repository. This requires a user who has access to the 20 | `trustwallet.blockchain` namespace on Ansible Galaxy to publish the build artifact. 21 | 22 | 5. Run the following commands to build and release the new version on Galaxy: 23 | ```shell 24 | ansible-galaxy collection build 25 | ansible-galaxy collection publish ./trustwallet-blockchain-$VERSION_HERE.tar.gz 26 | ``` 27 | 28 | After the version is published, verify it exists on the [Trust Wallet Collection](https://galaxy.ansible.com/trustwallet/blockchain) Ansible Galaxy page. 29 | -------------------------------------------------------------------------------- /docs/tron_role.md: -------------------------------------------------------------------------------- 1 | # TRON role 2 | 3 | Ansible role to manage TRON blockchain node. 4 | 5 | ## Requirements 6 | 7 | The TRON blockchain Java Archive can be launched on many operating systems. 8 | Check the project's GitHub repository to see the instructions for the 9 | operating system of your choice. But this role only targeting the Linux/Unix 10 | based OS. 11 | 12 | ### JDK 8 13 | 14 | This role requires the compatible Java runtime to be installed and assume it is present in the system. 15 | We've found that Amazon Corretto, production-ready distribution of OpenJDK, does the job well. 16 | 17 | Tip: JDK can be installed with Ansible Galaxy [lean_delivery.java](https://galaxy.ansible.com/lean_delivery/java) role or similar. 18 | 19 | ## Role Variables 20 | 21 | The role has default variables (see `defaults/main.yml`) which can be adjusted. 22 | 23 | ## Example Playbook 24 | 25 | By default, the role will use the [main_net_config.conf](https://github.com/tronprotocol/tron-deployment/blob/master/main_net_config.conf). 26 | But it can be replaced with the custom config. 27 | 28 | ```yaml 29 | - hosts: "all" 30 | gather_facts: true 31 | become: true 32 | 33 | roles: 34 | - role: lean_delivery.java 35 | java_distribution: corretto 36 | java_major_version: 8 37 | 38 | - role: trustwallet.blockchain.tron 39 | data_dir: /mnt/data # exampe of non-default data directory, default is /home/tron/.tron 40 | quicksync_mirror: oregon # options are oregon (default), frankfurt and singapore 41 | tron_config_override: ./config/main_net_config.conf # optional, set up a nodes with a new custom cofnig file 42 | ``` 43 | 44 | ## Development 45 | 46 | To aid in the development and testing of the Ansible role, we are 47 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 48 | 49 | ```shell 50 | molecule -v test -s tron 51 | ``` 52 | 53 | 54 | ## References 55 | 56 | * [Trust Wallet](https://trustwallet.com) 57 | * [TRON GitHub](https://github.com/tronprotocol/java-tron) 58 | * [TRON Full Node Deployment](https://developers.tron.network/docs/deploy-the-fullnode-or-supernode) 59 | * [TRON Full Node API](https://developers.tron.network/reference/full-node-api-overview) 60 | 61 | ## License 62 | 63 | MIT 64 | -------------------------------------------------------------------------------- /galaxy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: trustwallet 3 | name: blockchain 4 | version: 0.5.29 5 | readme: README.md 6 | authors: 7 | - vorotech 8 | description: | 9 | The Ansible Collection contains Trust Wallet's roles to manage blockchain nodes. 10 | Trust Wallet is a most trusted & secure crypto wallet. 11 | license_file: LICENSE 12 | tags: 13 | - blockbook 14 | - blockchain 15 | - cosmos 16 | - ethereum 17 | - nano 18 | - near 19 | - polygon 20 | - tron 21 | dependencies: 22 | community.general: ">=4.2.0" 23 | repository: https://github.com/trustwallet/ansible-collection-blockchain 24 | issues: https://github.com/trustwallet/ansible-collection-blockchain/issues 25 | homepage: https://trustwallet.com 26 | build_ignore: 27 | - molecule 28 | - venv 29 | - "*.tar.gz" 30 | -------------------------------------------------------------------------------- /meta/runtime.yml: -------------------------------------------------------------------------------- 1 | --- 2 | requires_ansible: ">=2.10.6" 3 | -------------------------------------------------------------------------------- /molecule/blockbook/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | become: true 5 | remote_user: admin 6 | 7 | tasks: 8 | - name: "Include roles/blockbook" 9 | ansible.builtin.include_role: 10 | name: roles/blockbook 11 | vars: 12 | chain_name: "{{ lookup('env', 'BLOCKBOOK_CHAIN_NAME') }}" 13 | blockbook_git_repo: https://github.com/vorotech/blockbook 14 | -------------------------------------------------------------------------------- /molecule/blockbook/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | no_log: "{{ molecule_no_log }}" 7 | collections: 8 | - community.aws 9 | vars: 10 | # Run config handling 11 | default_run_id: "{{ lookup('password', '/dev/null chars=ascii_lowercase length=5') }}" 12 | default_run_config: 13 | run_id: "{{ default_run_id }}" 14 | 15 | run_config_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/run-config.yml" 16 | run_config_from_file: "{{ (lookup('file', run_config_path, errors='ignore') or '{}') | from_yaml }}" 17 | run_config: "{{ default_run_config | combine(run_config_from_file) }}" 18 | 19 | # Platform settings handling 20 | default_aws_profile: "{{ lookup('env', 'AWS_PROFILE') }}" 21 | default_key_inject_method: cloud-init # valid values: [cloud-init, ec2] 22 | default_key_name: "molecule-{{ run_config.run_id }}" 23 | default_security_group_name: "molecule-{{ run_config.run_id }}" 24 | 25 | platform_defaults: 26 | aws_profile: "{{ default_aws_profile }}" 27 | key_inject_method: "{{ default_key_inject_method }}" 28 | key_name: "{{ default_key_name }}" 29 | region: "" 30 | security_group_name: "{{ default_security_group_name }}" 31 | security_groups: [] 32 | vpc_id: "" 33 | vpc_subnet_id: "" 34 | 35 | # Merging defaults into a list of dicts is, it turns out, not straightforward 36 | platforms: >- 37 | {{ [platform_defaults | dict2items] 38 | | product(molecule_yml.platforms | map('dict2items') | list) 39 | | map('flatten', levels=1) 40 | | list 41 | | map('items2dict') 42 | | list }} 43 | 44 | # Stored instance config 45 | instance_config: "{{ (lookup('file', molecule_instance_config, errors='ignore') or '{}') | from_yaml }}" 46 | pre_tasks: 47 | - name: Validate platform configurations 48 | ansible.builtin.assert: 49 | that: 50 | - platforms | length > 0 51 | - platform.name is string and platform.name | length > 0 52 | - platform.aws_profile is string 53 | - platform.key_inject_method is in ["cloud-init", "ec2"] 54 | - platform.key_name is string and platform.key_name | length > 0 55 | - platform.region is string 56 | - platform.security_group_name is string and platform.security_group_name | length > 0 57 | - platform.security_groups is sequence 58 | - platform.vpc_id is string 59 | - platform.vpc_subnet_id is string and platform.vpc_subnet_id | length > 0 60 | quiet: true 61 | loop: "{{ platforms }}" 62 | loop_control: 63 | loop_var: platform 64 | label: "{{ platform.name }}" 65 | tasks: 66 | - name: Look up subnets to determine VPCs (if needed) 67 | amazon.aws.ec2_vpc_subnet_info: 68 | subnet_ids: "{{ item.vpc_subnet_id }}" 69 | loop: "{{ platforms }}" 70 | loop_control: 71 | label: "{{ item.name }}" 72 | when: not item.vpc_id 73 | register: subnet_info 74 | 75 | - name: Validate discovered information 76 | ansible.builtin.assert: 77 | that: platform.vpc_id or (subnet_info.results[index].subnets | length > 0) 78 | quiet: true 79 | loop: "{{ platforms }}" 80 | loop_control: 81 | loop_var: platform 82 | index_var: index 83 | label: "{{ platform.name }}" 84 | 85 | - name: Destroy ephemeral EC2 instances 86 | amazon.aws.ec2_instance: 87 | profile: "{{ item.aws_profile | default(omit) }}" 88 | region: "{{ item.region | default(omit) }}" 89 | instance_ids: "{{ instance_config | map(attribute='instance_ids') | flatten }}" 90 | state: absent 91 | loop: "{{ platforms }}" 92 | loop_control: 93 | label: "{{ item.name }}" 94 | register: ec2_instances_async 95 | async: 7200 96 | poll: 0 97 | 98 | - name: Wait for instance destruction to complete 99 | ansible.builtin.async_status: 100 | jid: "{{ item.ansible_job_id }}" 101 | loop: "{{ ec2_instances_async.results }}" 102 | loop_control: 103 | index_var: index 104 | label: "{{ platforms[index].name }}" 105 | register: ec2_instances 106 | until: ec2_instances is finished 107 | retries: 300 108 | 109 | - name: Write Molecule instance configs 110 | ansible.builtin.copy: 111 | dest: "{{ molecule_instance_config }}" 112 | content: "{{ {} | to_yaml }}" 113 | mode: "644" 114 | 115 | - name: Destroy ephemeral security groups (if needed) 116 | amazon.aws.ec2_group: 117 | profile: "{{ item.aws_profile | default(omit) }}" 118 | region: "{{ item.region | default(omit) }}" 119 | vpc_id: "{{ item.vpc_id or vpc_subnet.vpc_id }}" 120 | name: "{{ item.security_group_name }}" 121 | state: absent 122 | vars: 123 | vpc_subnet: "{{ subnet_info.results[index].subnets[0] }}" 124 | loop: "{{ platforms }}" 125 | loop_control: 126 | index_var: index 127 | label: "{{ item.name }}" 128 | when: item.security_groups | length == 0 129 | 130 | - name: Destroy ephemeral keys (if needed) 131 | amazon.aws.ec2_key: 132 | profile: "{{ item.aws_profile | default(omit) }}" 133 | region: "{{ item.region | default(omit) }}" 134 | name: "{{ item.key_name }}" 135 | state: absent 136 | loop: "{{ platforms }}" 137 | loop_control: 138 | index_var: index 139 | label: "{{ item.name }}" 140 | when: item.key_inject_method == "ec2" 141 | -------------------------------------------------------------------------------- /molecule/blockbook/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: ec2 6 | lint: | 7 | set -e 8 | yamllint . 9 | ansible-lint . 10 | platforms: 11 | - name: instance 12 | image: ${MOLECULE_IMAGE_DEBIAN} 13 | image_owner: aws-marketplace 14 | instance_type: c5.2xlarge 15 | vpc_subnet_id: ${MOLECULE_VPC_SUBNET_ID} 16 | boot_wait_seconds: 10 17 | volumes: 18 | - device_name: xvda 19 | ebs: 20 | volume_size: 80 21 | delete_on_termination: true 22 | tags: 23 | Name: molecule-blockbook-${BLOCKBOOK_CHAIN_NAME} 24 | provisioner: 25 | name: ansible 26 | verifier: 27 | name: testinfra 28 | scenario: 29 | name: blockbook 30 | create_sequence: 31 | - create 32 | - prepare 33 | check_sequence: 34 | - destroy 35 | - create 36 | - prepare 37 | - converge 38 | - check 39 | - destroy 40 | converge_sequence: 41 | - create 42 | - prepare 43 | - converge 44 | destroy_sequence: 45 | - destroy 46 | test_sequence: 47 | - lint 48 | - destroy 49 | - syntax 50 | - create 51 | - prepare 52 | - converge 53 | - verify 54 | - destroy 55 | -------------------------------------------------------------------------------- /molecule/blockbook/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: all 4 | gather_facts: true 5 | become: true 6 | remote_user: admin 7 | 8 | pre_tasks: 9 | - name: "Check BLOCKBOOK_CHAIN_NAME env var is set" 10 | ansible.builtin.assert: 11 | that: lookup('env', 'BLOCKBOOK_CHAIN_NAME') | default("") | length > 0 12 | 13 | - name: "Make sure python3 is installed" 14 | ansible.builtin.package: 15 | name: python3 16 | 17 | roles: 18 | - role: geerlingguy.docker 19 | -------------------------------------------------------------------------------- /molecule/blockbook/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | chain_name = os.environ['BLOCKBOOK_CHAIN_NAME'] 4 | 5 | def pytest_collection_modifyitems(items, config): 6 | """ deselect test items which do not match the fixture chain """ 7 | deselection_items = [] 8 | for item in items: 9 | chains = set([mark.args[0] for mark in item.iter_markers(name='chain')]) 10 | if len(chains) > 0: 11 | if chain_name not in chains: 12 | deselection_items.append(item) 13 | items[:] = [item for item in items if item not in deselection_items] 14 | config.hook.pytest_deselected(items=deselection_items) 15 | -------------------------------------------------------------------------------- /molecule/blockbook/tests/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | markers = 3 | chain 4 | -------------------------------------------------------------------------------- /molecule/blockbook/tests/test_chain.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import testinfra.utils.ansible_runner 4 | 5 | from ansible.template import Templar 6 | from ansible.parsing.dataloader import DataLoader 7 | 8 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 9 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 10 | 11 | @pytest.mark.chain("firo") 12 | def test_blockbook_firo_running_and_enabled(host): 13 | s = host.service('blockbook-firo') 14 | assert s.is_enabled 15 | assert s.is_running 16 | 17 | @pytest.mark.chain("firo") 18 | def test_backend_firo_running_and_enabled(host): 19 | s = host.service('backend-firo') 20 | assert s.is_enabled 21 | assert s.is_running 22 | 23 | @pytest.mark.chain("firo") 24 | def test_http_is_listening(host): 25 | s = host.socket("tcp://0.0.0.0:9150") 26 | assert s.is_listening 27 | -------------------------------------------------------------------------------- /molecule/cosmos/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | tasks: 5 | - name: "Include roles/cosmos" 6 | ansible.builtin.include_role: 7 | name: roles/cosmos 8 | vars: 9 | ansible_become: true 10 | chain_name: "{{ lookup('env', 'COSMOS_CHAIN_NAME') }}" 11 | quicksync_mode: none 12 | -------------------------------------------------------------------------------- /molecule/cosmos/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | no_log: "{{ molecule_no_log }}" 7 | collections: 8 | - community.aws 9 | vars: 10 | # Run config handling 11 | default_run_id: "{{ lookup('password', '/dev/null chars=ascii_lowercase length=5') }}" 12 | default_run_config: 13 | run_id: "{{ default_run_id }}" 14 | 15 | run_config_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/run-config.yml" 16 | run_config_from_file: "{{ (lookup('file', run_config_path, errors='ignore') or '{}') | from_yaml }}" 17 | run_config: "{{ default_run_config | combine(run_config_from_file) }}" 18 | 19 | # Platform settings handling 20 | default_aws_profile: "{{ lookup('env', 'AWS_PROFILE') }}" 21 | default_key_inject_method: cloud-init # valid values: [cloud-init, ec2] 22 | default_key_name: "molecule-{{ run_config.run_id }}" 23 | default_security_group_name: "molecule-{{ run_config.run_id }}" 24 | 25 | platform_defaults: 26 | aws_profile: "{{ default_aws_profile }}" 27 | key_inject_method: "{{ default_key_inject_method }}" 28 | key_name: "{{ default_key_name }}" 29 | region: "" 30 | security_group_name: "{{ default_security_group_name }}" 31 | security_groups: [] 32 | vpc_id: "" 33 | vpc_subnet_id: "" 34 | 35 | # Merging defaults into a list of dicts is, it turns out, not straightforward 36 | platforms: >- 37 | {{ [platform_defaults | dict2items] 38 | | product(molecule_yml.platforms | map('dict2items') | list) 39 | | map('flatten', levels=1) 40 | | list 41 | | map('items2dict') 42 | | list }} 43 | 44 | # Stored instance config 45 | instance_config: "{{ (lookup('file', molecule_instance_config, errors='ignore') or '{}') | from_yaml }}" 46 | pre_tasks: 47 | - name: Validate platform configurations 48 | ansible.builtin.assert: 49 | that: 50 | - platforms | length > 0 51 | - platform.name is string and platform.name | length > 0 52 | - platform.aws_profile is string 53 | - platform.key_inject_method is in ["cloud-init", "ec2"] 54 | - platform.key_name is string and platform.key_name | length > 0 55 | - platform.region is string 56 | - platform.security_group_name is string and platform.security_group_name | length > 0 57 | - platform.security_groups is sequence 58 | - platform.vpc_id is string 59 | - platform.vpc_subnet_id is string and platform.vpc_subnet_id | length > 0 60 | quiet: true 61 | loop: "{{ platforms }}" 62 | loop_control: 63 | loop_var: platform 64 | label: "{{ platform.name }}" 65 | tasks: 66 | - name: Look up subnets to determine VPCs (if needed) 67 | amazon.aws.ec2_vpc_subnet_info: 68 | subnet_ids: "{{ item.vpc_subnet_id }}" 69 | loop: "{{ platforms }}" 70 | loop_control: 71 | label: "{{ item.name }}" 72 | when: not item.vpc_id 73 | register: subnet_info 74 | 75 | - name: Validate discovered information 76 | ansible.builtin.assert: 77 | that: platform.vpc_id or (subnet_info.results[index].subnets | length > 0) 78 | quiet: true 79 | loop: "{{ platforms }}" 80 | loop_control: 81 | loop_var: platform 82 | index_var: index 83 | label: "{{ platform.name }}" 84 | 85 | - name: Destroy ephemeral EC2 instances 86 | amazon.aws.ec2_instance: 87 | profile: "{{ item.aws_profile | default(omit) }}" 88 | region: "{{ item.region | default(omit) }}" 89 | instance_ids: "{{ instance_config | map(attribute='instance_ids') | flatten }}" 90 | state: absent 91 | loop: "{{ platforms }}" 92 | loop_control: 93 | label: "{{ item.name }}" 94 | register: ec2_instances_async 95 | async: 7200 96 | poll: 0 97 | 98 | - name: Wait for instance destruction to complete 99 | ansible.builtin.async_status: 100 | jid: "{{ item.ansible_job_id }}" 101 | loop: "{{ ec2_instances_async.results }}" 102 | loop_control: 103 | index_var: index 104 | label: "{{ platforms[index].name }}" 105 | register: ec2_instances 106 | until: ec2_instances is finished 107 | retries: 300 108 | 109 | - name: Write Molecule instance configs 110 | ansible.builtin.copy: 111 | dest: "{{ molecule_instance_config }}" 112 | content: "{{ {} | to_yaml }}" 113 | mode: "644" 114 | 115 | - name: Destroy ephemeral security groups (if needed) 116 | amazon.aws.ec2_group: 117 | profile: "{{ item.aws_profile | default(omit) }}" 118 | region: "{{ item.region | default(omit) }}" 119 | vpc_id: "{{ item.vpc_id or vpc_subnet.vpc_id }}" 120 | name: "{{ item.security_group_name }}" 121 | state: absent 122 | vars: 123 | vpc_subnet: "{{ subnet_info.results[index].subnets[0] }}" 124 | loop: "{{ platforms }}" 125 | loop_control: 126 | index_var: index 127 | label: "{{ item.name }}" 128 | when: item.security_groups | length == 0 129 | 130 | - name: Destroy ephemeral keys (if needed) 131 | amazon.aws.ec2_key: 132 | profile: "{{ item.aws_profile | default(omit) }}" 133 | region: "{{ item.region | default(omit) }}" 134 | name: "{{ item.key_name }}" 135 | state: absent 136 | loop: "{{ platforms }}" 137 | loop_control: 138 | index_var: index 139 | label: "{{ item.name }}" 140 | when: item.key_inject_method == "ec2" 141 | -------------------------------------------------------------------------------- /molecule/cosmos/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: ec2 6 | lint: | 7 | set -e 8 | yamllint . 9 | ansible-lint . 10 | platforms: 11 | - name: instance 12 | image: ${MOLECULE_IMAGE} 13 | instance_type: t3.medium 14 | vpc_subnet_id: ${MOLECULE_VPC_SUBNET_ID} 15 | boot_wait_seconds: 10 16 | volumes: 17 | - device_name: /dev/sda1 18 | ebs: 19 | volume_size: 80 20 | delete_on_termination: true 21 | tags: 22 | Name: molecule-cosmos-${COSMOS_CHAIN_NAME} 23 | provisioner: 24 | name: ansible 25 | verifier: 26 | name: testinfra 27 | scenario: 28 | name: cosmos 29 | create_sequence: 30 | - create 31 | - prepare 32 | check_sequence: 33 | - destroy 34 | - create 35 | - prepare 36 | - converge 37 | - check 38 | - destroy 39 | converge_sequence: 40 | - create 41 | - prepare 42 | - converge 43 | destroy_sequence: 44 | - destroy 45 | test_sequence: 46 | - lint 47 | - destroy 48 | - syntax 49 | - create 50 | - prepare 51 | - converge 52 | - verify 53 | - destroy 54 | -------------------------------------------------------------------------------- /molecule/cosmos/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: all 4 | gather_facts: true 5 | become: true 6 | 7 | pre_tasks: 8 | - name: "Check COSMOS_CHAIN_NAME env var is set" 9 | ansible.builtin.assert: 10 | that: lookup('env', 'COSMOS_CHAIN_NAME') | default("") | length > 0 11 | 12 | - name: "Make sure python3 is installed" 13 | ansible.builtin.package: 14 | name: python3 15 | 16 | roles: 17 | - role: telusdigital.unattended-upgrades 18 | - role: gantsign.golang 19 | golang_version: "1.18.1" 20 | golang_install_dir: /usr/local/go 21 | -------------------------------------------------------------------------------- /molecule/cosmos/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | chain_name = os.environ['COSMOS_CHAIN_NAME'] 4 | 5 | def pytest_collection_modifyitems(items, config): 6 | """ deselect test items which do not match the fixture chain """ 7 | deselection_items = [] 8 | for item in items: 9 | chains = set([mark.args[0] for mark in item.iter_markers(name='chain')]) 10 | if len(chains) > 0: 11 | if chain_name not in chains: 12 | deselection_items.append(item) 13 | items[:] = [item for item in items if item not in deselection_items] 14 | config.hook.pytest_deselected(items=deselection_items) 15 | -------------------------------------------------------------------------------- /molecule/cosmos/tests/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | markers = 3 | chain 4 | -------------------------------------------------------------------------------- /molecule/cosmos/tests/test_chain.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import testinfra.utils.ansible_runner 4 | 5 | from ansible.template import Templar 6 | from ansible.parsing.dataloader import DataLoader 7 | 8 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 9 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 10 | 11 | # https://github.com/pytest-dev/pytest-testinfra/issues/345 12 | # @pytest.fixture(scope='module') 13 | # def ansible_vars(host): 14 | # defaults_files = "file=../../roles/cosmos/defaults/main.yml" 15 | # vars_files = "file=../../roles/cosmos/vars/%s.yml" % chain_name 16 | 17 | # host.ansible("setup") 18 | # host.ansible("include_vars", defaults_files) 19 | # host.ansible("include_vars", vars_files) 20 | # all_vars = host.ansible.get_variables() 21 | # all_vars["ansible_play_host_all"] = testinfra_hosts 22 | # templar = Templar(loader=DataLoader(), variables=all_vars) 23 | 24 | # return templar.template(all_vars, fail_on_undefined=False) 25 | 26 | @pytest.mark.chain("osmosis") 27 | def test_osmosisd_running_and_enabled(host): 28 | s = host.service('osmosisd') 29 | assert s.is_enabled 30 | 31 | @pytest.mark.chain("terra") 32 | def test_terrad_running_and_enabled(host): 33 | s = host.service('terrad') 34 | assert s.is_enabled 35 | -------------------------------------------------------------------------------- /molecule/ethereum/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | tasks: 5 | - name: "Include roles/ethereum" 6 | ansible.builtin.include_role: 7 | name: roles/ethereum 8 | vars: 9 | ansible_become: true 10 | geth_config: 11 | Eth: 12 | SyncMode: light 13 | -------------------------------------------------------------------------------- /molecule/ethereum/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | no_log: "{{ molecule_no_log }}" 7 | collections: 8 | - community.aws 9 | vars: 10 | # Run config handling 11 | default_run_id: "{{ lookup('password', '/dev/null chars=ascii_lowercase length=5') }}" 12 | default_run_config: 13 | run_id: "{{ default_run_id }}" 14 | 15 | run_config_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/run-config.yml" 16 | run_config_from_file: "{{ (lookup('file', run_config_path, errors='ignore') or '{}') | from_yaml }}" 17 | run_config: "{{ default_run_config | combine(run_config_from_file) }}" 18 | 19 | # Platform settings handling 20 | default_aws_profile: "{{ lookup('env', 'AWS_PROFILE') }}" 21 | default_key_inject_method: cloud-init # valid values: [cloud-init, ec2] 22 | default_key_name: "molecule-{{ run_config.run_id }}" 23 | default_security_group_name: "molecule-{{ run_config.run_id }}" 24 | 25 | platform_defaults: 26 | aws_profile: "{{ default_aws_profile }}" 27 | key_inject_method: "{{ default_key_inject_method }}" 28 | key_name: "{{ default_key_name }}" 29 | region: "" 30 | security_group_name: "{{ default_security_group_name }}" 31 | security_groups: [] 32 | vpc_id: "" 33 | vpc_subnet_id: "" 34 | 35 | # Merging defaults into a list of dicts is, it turns out, not straightforward 36 | platforms: >- 37 | {{ [platform_defaults | dict2items] 38 | | product(molecule_yml.platforms | map('dict2items') | list) 39 | | map('flatten', levels=1) 40 | | list 41 | | map('items2dict') 42 | | list }} 43 | 44 | # Stored instance config 45 | instance_config: "{{ (lookup('file', molecule_instance_config, errors='ignore') or '{}') | from_yaml }}" 46 | pre_tasks: 47 | - name: Validate platform configurations 48 | ansible.builtin.assert: 49 | that: 50 | - platforms | length > 0 51 | - platform.name is string and platform.name | length > 0 52 | - platform.aws_profile is string 53 | - platform.key_inject_method is in ["cloud-init", "ec2"] 54 | - platform.key_name is string and platform.key_name | length > 0 55 | - platform.region is string 56 | - platform.security_group_name is string and platform.security_group_name | length > 0 57 | - platform.security_groups is sequence 58 | - platform.vpc_id is string 59 | - platform.vpc_subnet_id is string and platform.vpc_subnet_id | length > 0 60 | quiet: true 61 | loop: "{{ platforms }}" 62 | loop_control: 63 | loop_var: platform 64 | label: "{{ platform.name }}" 65 | tasks: 66 | - name: Look up subnets to determine VPCs (if needed) 67 | amazon.aws.ec2_vpc_subnet_info: 68 | subnet_ids: "{{ item.vpc_subnet_id }}" 69 | loop: "{{ platforms }}" 70 | loop_control: 71 | label: "{{ item.name }}" 72 | when: not item.vpc_id 73 | register: subnet_info 74 | 75 | - name: Validate discovered information 76 | ansible.builtin.assert: 77 | that: platform.vpc_id or (subnet_info.results[index].subnets | length > 0) 78 | quiet: true 79 | loop: "{{ platforms }}" 80 | loop_control: 81 | loop_var: platform 82 | index_var: index 83 | label: "{{ platform.name }}" 84 | 85 | - name: Destroy ephemeral EC2 instances 86 | amazon.aws.ec2_instance: 87 | profile: "{{ item.aws_profile | default(omit) }}" 88 | region: "{{ item.region | default(omit) }}" 89 | instance_ids: "{{ instance_config | map(attribute='instance_ids') | flatten }}" 90 | state: absent 91 | loop: "{{ platforms }}" 92 | loop_control: 93 | label: "{{ item.name }}" 94 | register: ec2_instances_async 95 | async: 7200 96 | poll: 0 97 | 98 | - name: Wait for instance destruction to complete 99 | ansible.builtin.async_status: 100 | jid: "{{ item.ansible_job_id }}" 101 | loop: "{{ ec2_instances_async.results }}" 102 | loop_control: 103 | index_var: index 104 | label: "{{ platforms[index].name }}" 105 | register: ec2_instances 106 | until: ec2_instances is finished 107 | retries: 300 108 | 109 | - name: Write Molecule instance configs 110 | ansible.builtin.copy: 111 | dest: "{{ molecule_instance_config }}" 112 | content: "{{ {} | to_yaml }}" 113 | mode: "644" 114 | 115 | - name: Destroy ephemeral security groups (if needed) 116 | amazon.aws.ec2_group: 117 | profile: "{{ item.aws_profile | default(omit) }}" 118 | region: "{{ item.region | default(omit) }}" 119 | vpc_id: "{{ item.vpc_id or vpc_subnet.vpc_id }}" 120 | name: "{{ item.security_group_name }}" 121 | state: absent 122 | vars: 123 | vpc_subnet: "{{ subnet_info.results[index].subnets[0] }}" 124 | loop: "{{ platforms }}" 125 | loop_control: 126 | index_var: index 127 | label: "{{ item.name }}" 128 | when: item.security_groups | length == 0 129 | 130 | - name: Destroy ephemeral keys (if needed) 131 | amazon.aws.ec2_key: 132 | profile: "{{ item.aws_profile | default(omit) }}" 133 | region: "{{ item.region | default(omit) }}" 134 | name: "{{ item.key_name }}" 135 | state: absent 136 | loop: "{{ platforms }}" 137 | loop_control: 138 | index_var: index 139 | label: "{{ item.name }}" 140 | when: item.key_inject_method == "ec2" 141 | -------------------------------------------------------------------------------- /molecule/ethereum/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: ec2 6 | lint: | 7 | set -e 8 | yamllint . 9 | ansible-lint . 10 | platforms: 11 | - name: instance 12 | image: ${MOLECULE_IMAGE} 13 | instance_type: t3.medium 14 | vpc_subnet_id: ${MOLECULE_VPC_SUBNET_ID} 15 | boot_wait_seconds: 10 16 | tags: 17 | Name: molecule-ethereum 18 | provisioner: 19 | name: ansible 20 | verifier: 21 | name: testinfra 22 | scenario: 23 | name: ethereum 24 | create_sequence: 25 | - create 26 | - prepare 27 | check_sequence: 28 | - destroy 29 | - create 30 | - prepare 31 | - converge 32 | - check 33 | - destroy 34 | converge_sequence: 35 | - create 36 | - prepare 37 | - converge 38 | destroy_sequence: 39 | - destroy 40 | test_sequence: 41 | - lint 42 | - destroy 43 | - syntax 44 | - create 45 | - prepare 46 | - converge 47 | - idempotence 48 | - verify 49 | - destroy 50 | -------------------------------------------------------------------------------- /molecule/ethereum/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: all 4 | gather_facts: true 5 | become: true 6 | 7 | pre_tasks: 8 | - name: Make sure python3 is installed 9 | ansible.builtin.package: 10 | name: python3 11 | -------------------------------------------------------------------------------- /molecule/ethereum/tests/test_chain.py: -------------------------------------------------------------------------------- 1 | import os 2 | import testinfra.utils.ansible_runner 3 | 4 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 5 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 6 | 7 | def test_geth_is_installed(host): 8 | p = host.package('geth') 9 | assert p.is_installed 10 | 11 | def test_gethd_running_and_enabled(host): 12 | s = host.service('gethd') 13 | assert s.is_enabled 14 | assert s.is_running 15 | 16 | def test_http_is_listening(host): 17 | s = host.socket("tcp://0.0.0.0:8545") 18 | assert s.is_listening -------------------------------------------------------------------------------- /molecule/nano/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | become: true 5 | 6 | tasks: 7 | - name: "Include roles/nano" 8 | ansible.builtin.include_role: 9 | name: roles/nano 10 | vars: 11 | nano_node: true 12 | nano_node_rpc_enable_control: true 13 | nano_rpc_cache: true 14 | nano_work_server: true 15 | # opencl_lib_path: "/usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.1" 16 | -------------------------------------------------------------------------------- /molecule/nano/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | no_log: "{{ molecule_no_log }}" 7 | collections: 8 | - community.aws 9 | vars: 10 | # Run config handling 11 | default_run_id: "{{ lookup('password', '/dev/null chars=ascii_lowercase length=5') }}" 12 | default_run_config: 13 | run_id: "{{ default_run_id }}" 14 | 15 | run_config_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/run-config.yml" 16 | run_config_from_file: "{{ (lookup('file', run_config_path, errors='ignore') or '{}') | from_yaml }}" 17 | run_config: "{{ default_run_config | combine(run_config_from_file) }}" 18 | 19 | # Platform settings handling 20 | default_aws_profile: "{{ lookup('env', 'AWS_PROFILE') }}" 21 | default_key_inject_method: cloud-init # valid values: [cloud-init, ec2] 22 | default_key_name: "molecule-{{ run_config.run_id }}" 23 | default_security_group_name: "molecule-{{ run_config.run_id }}" 24 | 25 | platform_defaults: 26 | aws_profile: "{{ default_aws_profile }}" 27 | key_inject_method: "{{ default_key_inject_method }}" 28 | key_name: "{{ default_key_name }}" 29 | region: "" 30 | security_group_name: "{{ default_security_group_name }}" 31 | security_groups: [] 32 | vpc_id: "" 33 | vpc_subnet_id: "" 34 | 35 | # Merging defaults into a list of dicts is, it turns out, not straightforward 36 | platforms: >- 37 | {{ [platform_defaults | dict2items] 38 | | product(molecule_yml.platforms | map('dict2items') | list) 39 | | map('flatten', levels=1) 40 | | list 41 | | map('items2dict') 42 | | list }} 43 | 44 | # Stored instance config 45 | instance_config: "{{ (lookup('file', molecule_instance_config, errors='ignore') or '{}') | from_yaml }}" 46 | pre_tasks: 47 | - name: Validate platform configurations 48 | ansible.builtin.assert: 49 | that: 50 | - platforms | length > 0 51 | - platform.name is string and platform.name | length > 0 52 | - platform.aws_profile is string 53 | - platform.key_inject_method is in ["cloud-init", "ec2"] 54 | - platform.key_name is string and platform.key_name | length > 0 55 | - platform.region is string 56 | - platform.security_group_name is string and platform.security_group_name | length > 0 57 | - platform.security_groups is sequence 58 | - platform.vpc_id is string 59 | - platform.vpc_subnet_id is string and platform.vpc_subnet_id | length > 0 60 | quiet: true 61 | loop: "{{ platforms }}" 62 | loop_control: 63 | loop_var: platform 64 | label: "{{ platform.name }}" 65 | tasks: 66 | - name: Look up subnets to determine VPCs (if needed) 67 | amazon.aws.ec2_vpc_subnet_info: 68 | subnet_ids: "{{ item.vpc_subnet_id }}" 69 | loop: "{{ platforms }}" 70 | loop_control: 71 | label: "{{ item.name }}" 72 | when: not item.vpc_id 73 | register: subnet_info 74 | 75 | - name: Validate discovered information 76 | ansible.builtin.assert: 77 | that: platform.vpc_id or (subnet_info.results[index].subnets | length > 0) 78 | quiet: true 79 | loop: "{{ platforms }}" 80 | loop_control: 81 | loop_var: platform 82 | index_var: index 83 | label: "{{ platform.name }}" 84 | 85 | - name: Destroy ephemeral EC2 instances 86 | amazon.aws.ec2_instance: 87 | profile: "{{ item.aws_profile | default(omit) }}" 88 | region: "{{ item.region | default(omit) }}" 89 | instance_ids: "{{ instance_config | map(attribute='instance_ids') | flatten }}" 90 | state: absent 91 | loop: "{{ platforms }}" 92 | loop_control: 93 | label: "{{ item.name }}" 94 | register: ec2_instances_async 95 | async: 7200 96 | poll: 0 97 | 98 | - name: Wait for instance destruction to complete 99 | ansible.builtin.async_status: 100 | jid: "{{ item.ansible_job_id }}" 101 | loop: "{{ ec2_instances_async.results }}" 102 | loop_control: 103 | index_var: index 104 | label: "{{ platforms[index].name }}" 105 | register: ec2_instances 106 | until: ec2_instances is finished 107 | retries: 300 108 | 109 | - name: Write Molecule instance configs 110 | ansible.builtin.copy: 111 | dest: "{{ molecule_instance_config }}" 112 | content: "{{ {} | to_yaml }}" 113 | mode: "644" 114 | 115 | - name: Destroy ephemeral security groups (if needed) 116 | amazon.aws.ec2_group: 117 | profile: "{{ item.aws_profile | default(omit) }}" 118 | region: "{{ item.region | default(omit) }}" 119 | vpc_id: "{{ item.vpc_id or vpc_subnet.vpc_id }}" 120 | name: "{{ item.security_group_name }}" 121 | state: absent 122 | vars: 123 | vpc_subnet: "{{ subnet_info.results[index].subnets[0] }}" 124 | loop: "{{ platforms }}" 125 | loop_control: 126 | index_var: index 127 | label: "{{ item.name }}" 128 | when: item.security_groups | length == 0 129 | 130 | - name: Destroy ephemeral keys (if needed) 131 | amazon.aws.ec2_key: 132 | profile: "{{ item.aws_profile | default(omit) }}" 133 | region: "{{ item.region | default(omit) }}" 134 | name: "{{ item.key_name }}" 135 | state: absent 136 | loop: "{{ platforms }}" 137 | loop_control: 138 | index_var: index 139 | label: "{{ item.name }}" 140 | when: item.key_inject_method == "ec2" 141 | -------------------------------------------------------------------------------- /molecule/nano/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: ec2 6 | lint: | 7 | set -e 8 | yamllint . 9 | ansible-lint . 10 | platforms: 11 | - name: instance 12 | image: ${MOLECULE_IMAGE} 13 | instance_type: g5.2xlarge 14 | vpc_subnet_id: ${MOLECULE_VPC_SUBNET_ID} 15 | boot_wait_seconds: 10 16 | volumes: 17 | - device_name: /dev/sda1 18 | ebs: 19 | volume_size: 80 20 | delete_on_termination: true 21 | tags: 22 | Name: molecule-nano 23 | provisioner: 24 | name: ansible 25 | verifier: 26 | name: testinfra 27 | scenario: 28 | name: nano 29 | create_sequence: 30 | - create 31 | - prepare 32 | check_sequence: 33 | - destroy 34 | - create 35 | - prepare 36 | - converge 37 | - check 38 | - destroy 39 | converge_sequence: 40 | - create 41 | - prepare 42 | - converge 43 | destroy_sequence: 44 | - destroy 45 | test_sequence: 46 | - lint 47 | - destroy 48 | - syntax 49 | - create 50 | - prepare 51 | - converge 52 | - verify 53 | - destroy 54 | -------------------------------------------------------------------------------- /molecule/nano/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: all 4 | gather_facts: true 5 | become: true 6 | 7 | pre_tasks: 8 | - name: "Make sure python3 is installed" 9 | ansible.builtin.package: 10 | update_cache: true 11 | cache_valid_time: 86400 12 | name: 13 | - python3 14 | - python3-pip 15 | 16 | roles: 17 | - role: telusdigital.unattended-upgrades 18 | 19 | - role: geerlingguy.docker 20 | 21 | - role: gantsign.golang 22 | golang_version: "1.18.1" 23 | golang_install_dir: /usr/local/go 24 | 25 | - role: nvidia.nvidia_driver 26 | nvidia_driver_branch: 470 27 | -------------------------------------------------------------------------------- /molecule/nano/tests/test_chain.py: -------------------------------------------------------------------------------- 1 | import os 2 | import testinfra.utils.ansible_runner 3 | 4 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 5 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 6 | 7 | def test_nano_rpc_cache_running_and_enabled(host): 8 | s = host.service('nano-rpc-cache') 9 | assert s.is_enabled 10 | assert s.is_running 11 | 12 | def test_nano_work_server_running_and_enabled(host): 13 | s = host.service('nano-work-server') 14 | assert s.is_enabled 15 | assert s.is_running 16 | 17 | def test_nano_p2p_is_listening(host): 18 | s = host.socket("tcp://0.0.0.0:7075") 19 | assert s.is_listening 20 | 21 | def test_nano_rpc_is_listening(host): 22 | s = host.socket("tcp://0.0.0.0:7076") 23 | assert s.is_listening 24 | 25 | def test_nano_rpc_cache_is_listening(host): 26 | s = host.socket("tcp://0.0.0.0:7376") 27 | assert s.is_listening 28 | 29 | def test_nano_work_server_is_listening(host): 30 | s = host.socket("tcp://0.0.0.0:7176") 31 | assert s.is_listening 32 | -------------------------------------------------------------------------------- /molecule/near/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | become: true 5 | 6 | tasks: 7 | - name: "Include roles/near" 8 | ansible.builtin.include_role: 9 | name: roles/near 10 | vars: 11 | quicksync_mode: none 12 | -------------------------------------------------------------------------------- /molecule/near/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | no_log: "{{ molecule_no_log }}" 7 | collections: 8 | - community.aws 9 | vars: 10 | # Run config handling 11 | default_run_id: "{{ lookup('password', '/dev/null chars=ascii_lowercase length=5') }}" 12 | default_run_config: 13 | run_id: "{{ default_run_id }}" 14 | 15 | run_config_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/run-config.yml" 16 | run_config_from_file: "{{ (lookup('file', run_config_path, errors='ignore') or '{}') | from_yaml }}" 17 | run_config: "{{ default_run_config | combine(run_config_from_file) }}" 18 | 19 | # Platform settings handling 20 | default_aws_profile: "{{ lookup('env', 'AWS_PROFILE') }}" 21 | default_key_inject_method: cloud-init # valid values: [cloud-init, ec2] 22 | default_key_name: "molecule-{{ run_config.run_id }}" 23 | default_security_group_name: "molecule-{{ run_config.run_id }}" 24 | 25 | platform_defaults: 26 | aws_profile: "{{ default_aws_profile }}" 27 | key_inject_method: "{{ default_key_inject_method }}" 28 | key_name: "{{ default_key_name }}" 29 | region: "" 30 | security_group_name: "{{ default_security_group_name }}" 31 | security_groups: [] 32 | vpc_id: "" 33 | vpc_subnet_id: "" 34 | 35 | # Merging defaults into a list of dicts is, it turns out, not straightforward 36 | platforms: >- 37 | {{ [platform_defaults | dict2items] 38 | | product(molecule_yml.platforms | map('dict2items') | list) 39 | | map('flatten', levels=1) 40 | | list 41 | | map('items2dict') 42 | | list }} 43 | 44 | # Stored instance config 45 | instance_config: "{{ (lookup('file', molecule_instance_config, errors='ignore') or '{}') | from_yaml }}" 46 | pre_tasks: 47 | - name: Validate platform configurations 48 | ansible.builtin.assert: 49 | that: 50 | - platforms | length > 0 51 | - platform.name is string and platform.name | length > 0 52 | - platform.aws_profile is string 53 | - platform.key_inject_method is in ["cloud-init", "ec2"] 54 | - platform.key_name is string and platform.key_name | length > 0 55 | - platform.region is string 56 | - platform.security_group_name is string and platform.security_group_name | length > 0 57 | - platform.security_groups is sequence 58 | - platform.vpc_id is string 59 | - platform.vpc_subnet_id is string and platform.vpc_subnet_id | length > 0 60 | quiet: true 61 | loop: "{{ platforms }}" 62 | loop_control: 63 | loop_var: platform 64 | label: "{{ platform.name }}" 65 | tasks: 66 | - name: Look up subnets to determine VPCs (if needed) 67 | amazon.aws.ec2_vpc_subnet_info: 68 | subnet_ids: "{{ item.vpc_subnet_id }}" 69 | loop: "{{ platforms }}" 70 | loop_control: 71 | label: "{{ item.name }}" 72 | when: not item.vpc_id 73 | register: subnet_info 74 | 75 | - name: Validate discovered information 76 | ansible.builtin.assert: 77 | that: platform.vpc_id or (subnet_info.results[index].subnets | length > 0) 78 | quiet: true 79 | loop: "{{ platforms }}" 80 | loop_control: 81 | loop_var: platform 82 | index_var: index 83 | label: "{{ platform.name }}" 84 | 85 | - name: Destroy ephemeral EC2 instances 86 | amazon.aws.ec2_instance: 87 | profile: "{{ item.aws_profile | default(omit) }}" 88 | region: "{{ item.region | default(omit) }}" 89 | instance_ids: "{{ instance_config | map(attribute='instance_ids') | flatten }}" 90 | state: absent 91 | loop: "{{ platforms }}" 92 | loop_control: 93 | label: "{{ item.name }}" 94 | register: ec2_instances_async 95 | async: 7200 96 | poll: 0 97 | 98 | - name: Wait for instance destruction to complete 99 | ansible.builtin.async_status: 100 | jid: "{{ item.ansible_job_id }}" 101 | loop: "{{ ec2_instances_async.results }}" 102 | loop_control: 103 | index_var: index 104 | label: "{{ platforms[index].name }}" 105 | register: ec2_instances 106 | until: ec2_instances is finished 107 | retries: 300 108 | 109 | - name: Write Molecule instance configs 110 | ansible.builtin.copy: 111 | dest: "{{ molecule_instance_config }}" 112 | content: "{{ {} | to_yaml }}" 113 | mode: "644" 114 | 115 | - name: Destroy ephemeral security groups (if needed) 116 | amazon.aws.ec2_group: 117 | profile: "{{ item.aws_profile | default(omit) }}" 118 | region: "{{ item.region | default(omit) }}" 119 | vpc_id: "{{ item.vpc_id or vpc_subnet.vpc_id }}" 120 | name: "{{ item.security_group_name }}" 121 | state: absent 122 | vars: 123 | vpc_subnet: "{{ subnet_info.results[index].subnets[0] }}" 124 | loop: "{{ platforms }}" 125 | loop_control: 126 | index_var: index 127 | label: "{{ item.name }}" 128 | when: item.security_groups | length == 0 129 | 130 | - name: Destroy ephemeral keys (if needed) 131 | amazon.aws.ec2_key: 132 | profile: "{{ item.aws_profile | default(omit) }}" 133 | region: "{{ item.region | default(omit) }}" 134 | name: "{{ item.key_name }}" 135 | state: absent 136 | loop: "{{ platforms }}" 137 | loop_control: 138 | index_var: index 139 | label: "{{ item.name }}" 140 | when: item.key_inject_method == "ec2" 141 | -------------------------------------------------------------------------------- /molecule/near/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: ec2 6 | lint: | 7 | set -e 8 | yamllint . 9 | ansible-lint . 10 | platforms: 11 | - name: instance 12 | image: ${MOLECULE_IMAGE} 13 | instance_type: c5.2xlarge 14 | vpc_subnet_id: ${MOLECULE_VPC_SUBNET_ID} 15 | boot_wait_seconds: 10 16 | volumes: 17 | - device_name: /dev/sda1 18 | ebs: 19 | volume_size: 80 20 | delete_on_termination: true 21 | tags: 22 | Name: molecule-near 23 | provisioner: 24 | name: ansible 25 | verifier: 26 | name: testinfra 27 | scenario: 28 | name: near 29 | create_sequence: 30 | - create 31 | - prepare 32 | check_sequence: 33 | - destroy 34 | - create 35 | - prepare 36 | - converge 37 | - check 38 | - destroy 39 | converge_sequence: 40 | - create 41 | - prepare 42 | - converge 43 | destroy_sequence: 44 | - destroy 45 | test_sequence: 46 | - lint 47 | - destroy 48 | - syntax 49 | - create 50 | - prepare 51 | - converge 52 | - verify 53 | - destroy 54 | -------------------------------------------------------------------------------- /molecule/near/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Prepare" 3 | hosts: all 4 | gather_facts: true 5 | become: true 6 | 7 | pre_tasks: 8 | - name: "Make sure python3 is installed" 9 | ansible.builtin.package: 10 | update_cache: true 11 | cache_valid_time: 86400 12 | name: 13 | - python3 14 | - python3-pip 15 | 16 | roles: 17 | - role: telusdigital.unattended-upgrades 18 | - role: geerlingguy.docker 19 | -------------------------------------------------------------------------------- /molecule/near/tests/test_chain.py: -------------------------------------------------------------------------------- 1 | import os 2 | import testinfra.utils.ansible_runner 3 | 4 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 5 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 6 | 7 | def test_neard_running_and_enabled(host): 8 | s = host.service('neard') 9 | assert s.is_running 10 | assert s.is_enabled 11 | 12 | def test_http_is_listening(host): 13 | s = host.socket("tcp://0.0.0.0:3030") 14 | assert s.is_listening 15 | -------------------------------------------------------------------------------- /molecule/polygon/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | become: true 5 | 6 | tasks: 7 | - name: "Include roles/polygon" 8 | ansible.builtin.include_role: 9 | name: roles/polygon 10 | vars: 11 | quicksync_mode: none 12 | eth_rpc_url: https://rpc.ankr.com/eth 13 | -------------------------------------------------------------------------------- /molecule/polygon/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | no_log: "{{ molecule_no_log }}" 7 | collections: 8 | - community.aws 9 | vars: 10 | # Run config handling 11 | default_run_id: "{{ lookup('password', '/dev/null chars=ascii_lowercase length=5') }}" 12 | default_run_config: 13 | run_id: "{{ default_run_id }}" 14 | 15 | run_config_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/run-config.yml" 16 | run_config_from_file: "{{ (lookup('file', run_config_path, errors='ignore') or '{}') | from_yaml }}" 17 | run_config: "{{ default_run_config | combine(run_config_from_file) }}" 18 | 19 | # Platform settings handling 20 | default_aws_profile: "{{ lookup('env', 'AWS_PROFILE') }}" 21 | default_key_inject_method: cloud-init # valid values: [cloud-init, ec2] 22 | default_key_name: "molecule-{{ run_config.run_id }}" 23 | default_security_group_name: "molecule-{{ run_config.run_id }}" 24 | 25 | platform_defaults: 26 | aws_profile: "{{ default_aws_profile }}" 27 | key_inject_method: "{{ default_key_inject_method }}" 28 | key_name: "{{ default_key_name }}" 29 | region: "" 30 | security_group_name: "{{ default_security_group_name }}" 31 | security_groups: [] 32 | vpc_id: "" 33 | vpc_subnet_id: "" 34 | 35 | # Merging defaults into a list of dicts is, it turns out, not straightforward 36 | platforms: >- 37 | {{ [platform_defaults | dict2items] 38 | | product(molecule_yml.platforms | map('dict2items') | list) 39 | | map('flatten', levels=1) 40 | | list 41 | | map('items2dict') 42 | | list }} 43 | 44 | # Stored instance config 45 | instance_config: "{{ (lookup('file', molecule_instance_config, errors='ignore') or '{}') | from_yaml }}" 46 | pre_tasks: 47 | - name: Validate platform configurations 48 | ansible.builtin.assert: 49 | that: 50 | - platforms | length > 0 51 | - platform.name is string and platform.name | length > 0 52 | - platform.aws_profile is string 53 | - platform.key_inject_method is in ["cloud-init", "ec2"] 54 | - platform.key_name is string and platform.key_name | length > 0 55 | - platform.region is string 56 | - platform.security_group_name is string and platform.security_group_name | length > 0 57 | - platform.security_groups is sequence 58 | - platform.vpc_id is string 59 | - platform.vpc_subnet_id is string and platform.vpc_subnet_id | length > 0 60 | quiet: true 61 | loop: "{{ platforms }}" 62 | loop_control: 63 | loop_var: platform 64 | label: "{{ platform.name }}" 65 | tasks: 66 | - name: Look up subnets to determine VPCs (if needed) 67 | amazon.aws.ec2_vpc_subnet_info: 68 | subnet_ids: "{{ item.vpc_subnet_id }}" 69 | loop: "{{ platforms }}" 70 | loop_control: 71 | label: "{{ item.name }}" 72 | when: not item.vpc_id 73 | register: subnet_info 74 | 75 | - name: Validate discovered information 76 | ansible.builtin.assert: 77 | that: platform.vpc_id or (subnet_info.results[index].subnets | length > 0) 78 | quiet: true 79 | loop: "{{ platforms }}" 80 | loop_control: 81 | loop_var: platform 82 | index_var: index 83 | label: "{{ platform.name }}" 84 | 85 | - name: Destroy ephemeral EC2 instances 86 | amazon.aws.ec2_instance: 87 | profile: "{{ item.aws_profile | default(omit) }}" 88 | region: "{{ item.region | default(omit) }}" 89 | instance_ids: "{{ instance_config | map(attribute='instance_ids') | flatten }}" 90 | state: absent 91 | loop: "{{ platforms }}" 92 | loop_control: 93 | label: "{{ item.name }}" 94 | register: ec2_instances_async 95 | async: 7200 96 | poll: 0 97 | 98 | - name: Wait for instance destruction to complete 99 | ansible.builtin.async_status: 100 | jid: "{{ item.ansible_job_id }}" 101 | loop: "{{ ec2_instances_async.results }}" 102 | loop_control: 103 | index_var: index 104 | label: "{{ platforms[index].name }}" 105 | register: ec2_instances 106 | until: ec2_instances is finished 107 | retries: 300 108 | 109 | - name: Write Molecule instance configs 110 | ansible.builtin.copy: 111 | dest: "{{ molecule_instance_config }}" 112 | content: "{{ {} | to_yaml }}" 113 | mode: "644" 114 | 115 | - name: Destroy ephemeral security groups (if needed) 116 | amazon.aws.ec2_group: 117 | profile: "{{ item.aws_profile | default(omit) }}" 118 | region: "{{ item.region | default(omit) }}" 119 | vpc_id: "{{ item.vpc_id or vpc_subnet.vpc_id }}" 120 | name: "{{ item.security_group_name }}" 121 | state: absent 122 | vars: 123 | vpc_subnet: "{{ subnet_info.results[index].subnets[0] }}" 124 | loop: "{{ platforms }}" 125 | loop_control: 126 | index_var: index 127 | label: "{{ item.name }}" 128 | when: item.security_groups | length == 0 129 | 130 | - name: Destroy ephemeral keys (if needed) 131 | amazon.aws.ec2_key: 132 | profile: "{{ item.aws_profile | default(omit) }}" 133 | region: "{{ item.region | default(omit) }}" 134 | name: "{{ item.key_name }}" 135 | state: absent 136 | loop: "{{ platforms }}" 137 | loop_control: 138 | index_var: index 139 | label: "{{ item.name }}" 140 | when: item.key_inject_method == "ec2" 141 | -------------------------------------------------------------------------------- /molecule/polygon/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: ec2 6 | lint: | 7 | set -e 8 | yamllint . 9 | ansible-lint . 10 | platforms: 11 | - name: instance 12 | image: ${MOLECULE_IMAGE} 13 | instance_type: t3.xlarge 14 | vpc_subnet_id: ${MOLECULE_VPC_SUBNET_ID} 15 | boot_wait_seconds: 10 16 | volumes: 17 | - device_name: /dev/sda1 18 | ebs: 19 | volume_size: 80 20 | delete_on_termination: true 21 | tags: 22 | Name: molecule-polygon 23 | provisioner: 24 | name: ansible 25 | verifier: 26 | name: testinfra 27 | scenario: 28 | name: polygon 29 | create_sequence: 30 | - create 31 | - prepare 32 | check_sequence: 33 | - destroy 34 | - create 35 | - prepare 36 | - converge 37 | - check 38 | - destroy 39 | converge_sequence: 40 | - create 41 | - prepare 42 | - converge 43 | destroy_sequence: 44 | - destroy 45 | test_sequence: 46 | - lint 47 | - destroy 48 | - syntax 49 | - create 50 | - prepare 51 | - converge 52 | - verify 53 | - destroy 54 | -------------------------------------------------------------------------------- /molecule/polygon/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Prepare" 3 | hosts: all 4 | gather_facts: true 5 | become: true 6 | 7 | pre_tasks: 8 | - name: "Make sure python3 is installed" 9 | ansible.builtin.apt: 10 | update_cache: true 11 | cache_valid_time: 86400 12 | pkg: python3 13 | 14 | roles: 15 | - role: telusdigital.unattended-upgrades 16 | 17 | - role: gantsign.golang 18 | golang_version: "1.18.1" 19 | golang_install_dir: /usr/local/go 20 | -------------------------------------------------------------------------------- /molecule/polygon/tests/test_chain.py: -------------------------------------------------------------------------------- 1 | import os 2 | import testinfra.utils.ansible_runner 3 | 4 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 5 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 6 | 7 | def test_heimdall_running_and_enabled(host): 8 | s = host.service('heimdalld') 9 | assert s.is_running 10 | assert s.is_enabled 11 | 12 | def test_heimdalld_p2p_is_listening(host): 13 | s = host.socket("tcp://0.0.0.0:26656") 14 | assert s.is_listening 15 | 16 | def test_heimdall_rest_server_running_and_enabled(host): 17 | s = host.service('heimdalld-rest-server') 18 | assert s.is_running 19 | assert s.is_enabled 20 | 21 | def test_heimdalld_rest_server_is_listening(host): 22 | s = host.socket("tcp://0.0.0.0:1317") 23 | assert s.is_listening 24 | 25 | def test_bor_running_and_enabled(host): 26 | s = host.service('bor') 27 | assert s.is_running 28 | assert s.is_enabled 29 | 30 | def test_bor_p2p_is_listening(host): 31 | s = host.socket("tcp://0.0.0.0:30303") 32 | assert s.is_listening 33 | 34 | def test_bor_rpc_is_listening(host): 35 | s = host.socket("tcp://0.0.0.0:8545") 36 | assert s.is_listening 37 | -------------------------------------------------------------------------------- /molecule/tron/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | 5 | tasks: 6 | - name: "Include roles/tron" 7 | ansible.builtin.include_role: 8 | name: roles/tron 9 | vars: 10 | quicksync_enabled: false 11 | tron_config_override: ./config/main_net_config.conf 12 | -------------------------------------------------------------------------------- /molecule/tron/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Destroy 3 | hosts: localhost 4 | connection: local 5 | gather_facts: false 6 | no_log: "{{ molecule_no_log }}" 7 | collections: 8 | - community.aws 9 | vars: 10 | # Run config handling 11 | default_run_id: "{{ lookup('password', '/dev/null chars=ascii_lowercase length=5') }}" 12 | default_run_config: 13 | run_id: "{{ default_run_id }}" 14 | 15 | run_config_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/run-config.yml" 16 | run_config_from_file: "{{ (lookup('file', run_config_path, errors='ignore') or '{}') | from_yaml }}" 17 | run_config: "{{ default_run_config | combine(run_config_from_file) }}" 18 | 19 | # Platform settings handling 20 | default_aws_profile: "{{ lookup('env', 'AWS_PROFILE') }}" 21 | default_key_inject_method: cloud-init # valid values: [cloud-init, ec2] 22 | default_key_name: "molecule-{{ run_config.run_id }}" 23 | default_security_group_name: "molecule-{{ run_config.run_id }}" 24 | 25 | platform_defaults: 26 | aws_profile: "{{ default_aws_profile }}" 27 | key_inject_method: "{{ default_key_inject_method }}" 28 | key_name: "{{ default_key_name }}" 29 | region: "" 30 | security_group_name: "{{ default_security_group_name }}" 31 | security_groups: [] 32 | vpc_id: "" 33 | vpc_subnet_id: "" 34 | 35 | # Merging defaults into a list of dicts is, it turns out, not straightforward 36 | platforms: >- 37 | {{ [platform_defaults | dict2items] 38 | | product(molecule_yml.platforms | map('dict2items') | list) 39 | | map('flatten', levels=1) 40 | | list 41 | | map('items2dict') 42 | | list }} 43 | 44 | # Stored instance config 45 | instance_config: "{{ (lookup('file', molecule_instance_config, errors='ignore') or '{}') | from_yaml }}" 46 | pre_tasks: 47 | - name: Validate platform configurations 48 | ansible.builtin.assert: 49 | that: 50 | - platforms | length > 0 51 | - platform.name is string and platform.name | length > 0 52 | - platform.aws_profile is string 53 | - platform.key_inject_method is in ["cloud-init", "ec2"] 54 | - platform.key_name is string and platform.key_name | length > 0 55 | - platform.region is string 56 | - platform.security_group_name is string and platform.security_group_name | length > 0 57 | - platform.security_groups is sequence 58 | - platform.vpc_id is string 59 | - platform.vpc_subnet_id is string and platform.vpc_subnet_id | length > 0 60 | quiet: true 61 | loop: "{{ platforms }}" 62 | loop_control: 63 | loop_var: platform 64 | label: "{{ platform.name }}" 65 | tasks: 66 | - name: Look up subnets to determine VPCs (if needed) 67 | amazon.aws.ec2_vpc_subnet_info: 68 | subnet_ids: "{{ item.vpc_subnet_id }}" 69 | loop: "{{ platforms }}" 70 | loop_control: 71 | label: "{{ item.name }}" 72 | when: not item.vpc_id 73 | register: subnet_info 74 | 75 | - name: Validate discovered information 76 | ansible.builtin.assert: 77 | that: platform.vpc_id or (subnet_info.results[index].subnets | length > 0) 78 | quiet: true 79 | loop: "{{ platforms }}" 80 | loop_control: 81 | loop_var: platform 82 | index_var: index 83 | label: "{{ platform.name }}" 84 | 85 | - name: Destroy ephemeral EC2 instances 86 | amazon.aws.ec2_instance: 87 | profile: "{{ item.aws_profile | default(omit) }}" 88 | region: "{{ item.region | default(omit) }}" 89 | instance_ids: "{{ instance_config | map(attribute='instance_ids') | flatten }}" 90 | state: absent 91 | loop: "{{ platforms }}" 92 | loop_control: 93 | label: "{{ item.name }}" 94 | register: ec2_instances_async 95 | async: 7200 96 | poll: 0 97 | 98 | - name: Wait for instance destruction to complete 99 | ansible.builtin.async_status: 100 | jid: "{{ item.ansible_job_id }}" 101 | loop: "{{ ec2_instances_async.results }}" 102 | loop_control: 103 | index_var: index 104 | label: "{{ platforms[index].name }}" 105 | register: ec2_instances 106 | until: ec2_instances is finished 107 | retries: 300 108 | 109 | - name: Write Molecule instance configs 110 | ansible.builtin.copy: 111 | dest: "{{ molecule_instance_config }}" 112 | content: "{{ {} | to_yaml }}" 113 | mode: "644" 114 | 115 | - name: Destroy ephemeral security groups (if needed) 116 | amazon.aws.ec2_group: 117 | profile: "{{ item.aws_profile | default(omit) }}" 118 | region: "{{ item.region | default(omit) }}" 119 | vpc_id: "{{ item.vpc_id or vpc_subnet.vpc_id }}" 120 | name: "{{ item.security_group_name }}" 121 | state: absent 122 | vars: 123 | vpc_subnet: "{{ subnet_info.results[index].subnets[0] }}" 124 | loop: "{{ platforms }}" 125 | loop_control: 126 | index_var: index 127 | label: "{{ item.name }}" 128 | when: item.security_groups | length == 0 129 | 130 | - name: Destroy ephemeral keys (if needed) 131 | amazon.aws.ec2_key: 132 | profile: "{{ item.aws_profile | default(omit) }}" 133 | region: "{{ item.region | default(omit) }}" 134 | name: "{{ item.key_name }}" 135 | state: absent 136 | loop: "{{ platforms }}" 137 | loop_control: 138 | index_var: index 139 | label: "{{ item.name }}" 140 | when: item.key_inject_method == "ec2" 141 | -------------------------------------------------------------------------------- /molecule/tron/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: ec2 6 | lint: | 7 | set -e 8 | yamllint . 9 | ansible-lint . 10 | platforms: 11 | - name: instance 12 | image: ${MOLECULE_IMAGE} 13 | instance_type: c5.2xlarge 14 | vpc_subnet_id: ${MOLECULE_VPC_SUBNET_ID} 15 | boot_wait_seconds: 10 16 | volumes: 17 | - device_name: /dev/sda1 18 | ebs: 19 | volume_size: 80 20 | delete_on_termination: true 21 | tags: 22 | Name: molecule-tron 23 | provisioner: 24 | name: ansible 25 | verifier: 26 | name: testinfra 27 | scenario: 28 | name: tron 29 | create_sequence: 30 | - create 31 | - prepare 32 | check_sequence: 33 | - destroy 34 | - create 35 | - prepare 36 | - converge 37 | - check 38 | - destroy 39 | converge_sequence: 40 | - create 41 | - prepare 42 | - converge 43 | destroy_sequence: 44 | - destroy 45 | test_sequence: 46 | - lint 47 | - destroy 48 | - syntax 49 | - create 50 | - prepare 51 | - converge 52 | - verify 53 | - destroy 54 | -------------------------------------------------------------------------------- /molecule/tron/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Prepare" 3 | hosts: all 4 | gather_facts: true 5 | become: true 6 | 7 | pre_tasks: 8 | - name: "Make sure python3 is installed" 9 | ansible.builtin.package: 10 | update_cache: true 11 | cache_valid_time: 86400 12 | name: 13 | - python3 14 | - python3-pip 15 | 16 | roles: 17 | - role: telusdigital.unattended-upgrades 18 | 19 | - role: lean_delivery.java 20 | java_distribution: corretto 21 | java_major_version: 8 22 | -------------------------------------------------------------------------------- /molecule/tron/tests/test_chain.py: -------------------------------------------------------------------------------- 1 | import os 2 | import testinfra.utils.ansible_runner 3 | 4 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 5 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 6 | 7 | def test_neard_running_and_enabled(host): 8 | s = host.service('trond') 9 | assert s.is_running 10 | assert s.is_enabled 11 | 12 | def test_http_is_listening(host): 13 | s = host.socket("tcp://0.0.0.0:8090") 14 | assert s.is_listening 15 | 16 | def test_p2p_tcp_is_listening(host): 17 | s = host.socket("tcp://0.0.0.0:18888") 18 | assert s.is_listening 19 | 20 | def test_p2p_udp_is_listening(host): 21 | s = host.socket("udp://0.0.0.0:18888") 22 | assert s.is_listening 23 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # Collections Plugins Directory 2 | 3 | This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that 4 | is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that 5 | would contain module utils and modules respectively. 6 | 7 | Here is an example directory of the majority of plugins currently supported by Ansible: 8 | 9 | ``` 10 | └── plugins 11 | ├── action 12 | ├── become 13 | ├── cache 14 | ├── callback 15 | ├── cliconf 16 | ├── connection 17 | ├── filter 18 | ├── httpapi 19 | ├── inventory 20 | ├── lookup 21 | ├── module_utils 22 | ├── modules 23 | ├── netconf 24 | ├── shell 25 | ├── strategy 26 | ├── terminal 27 | ├── test 28 | └── vars 29 | ``` 30 | 31 | A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible-core/2.12/plugins/plugins.html). 32 | -------------------------------------------------------------------------------- /plugins/filter/filters.py: -------------------------------------------------------------------------------- 1 | def to_cosmos_peers(peers): 2 | result = [] 3 | for peer in peers: 4 | result.append("%s@%s" % (peer['id'], peer['address'])) 5 | 6 | return ','.join(result) 7 | 8 | class FilterModule(object): 9 | 10 | def filters(self): 11 | return { 12 | 'to_cosmos_peers': to_cosmos_peers, 13 | } 14 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ansible-compat==1.0.0 2 | ansible-core==2.12.1 3 | ansible-lint==5.3.2 4 | ansible==7.0.0 5 | boto3==1.20.38 6 | botocore==1.23.38 7 | flake8==4.0.1 8 | jinja2>=2.11.2 9 | molecule-ec2==0.4 10 | molecule==3.5.2 11 | pytest-testinfra==6.5.0 12 | yamllint==1.26.3 -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | roles: 3 | - name: gantsign.golang 4 | - name: geerlingguy.docker 5 | - name: lean_delivery.java 6 | - name: telusdigital.unattended-upgrades 7 | - name: nvidia.nvidia_driver 8 | collections: 9 | - name: amazon.aws 10 | - name: community.general 11 | -------------------------------------------------------------------------------- /roles/blockbook/README.md: -------------------------------------------------------------------------------- 1 | # Blockbook Role 2 | 3 | Ansible role to manage [Blockbook](https://github.com/trezor/blockbook) supported blockchains. 4 | 5 | ## Requirements 6 | 7 | Officially supported platform is Debian Linux and AMD64 architecture. 8 | 9 | ### Docker CE 10 | 11 | This role doesn't attempt to install the Docker CE, but assumes it is available. 12 | 13 | Tip: Docker can be installed with Ansible Galaxy (geerlingguy.docker)[https://galaxy.ansible.com/geerlingguy/docker] role or similar. 14 | 15 | ## Role Variables 16 | 17 | The role has default variables (see `defaults/main.yml`) which can be adjusted. 18 | 19 | ## Example Playbook 20 | 21 | ```yaml 22 | --- 23 | - hosts: "all" 24 | gather_facts: true 25 | become: true 26 | remote_user: admin 27 | 28 | roles: 29 | - role: geerlingguy.docker 30 | 31 | - role: trustwallet.blockchain.blockbook 32 | chain_name: firo 33 | data_dir: /mnt/data/ # example of custom data_dir, default is /root 34 | ``` 35 | 36 | ## Development 37 | 38 | To aid in the development and testing of the Ansible role, we are 39 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 40 | 41 | The role to test the Blockbook supported blockchains expects the `BLOCKBOOK_CHAIN_NAME` parameter 42 | to be set as environment variable. 43 | 44 | ```shell 45 | BLOCKBOOK_CHAIN_NAME=firo molecule -v test -s blockbook 46 | ``` 47 | 48 | ## References 49 | 50 | * [Trust Wallet](https://trustwallet.com) 51 | * [Blockbook](https://github.com/trezor/blockbook) 52 | 53 | ## License 54 | 55 | MIT 56 | -------------------------------------------------------------------------------- /roles/blockbook/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defines the a supporter Blockbook chain to init 3 | # Requred to be set in role vars. 4 | chain_name: "" 5 | 6 | blockbook_git_repo: https://github.com/trezor/blockbook 7 | 8 | blockbook_git_version: master 9 | 10 | blockbook_checkout_dir: "/tmp/blockbook" 11 | 12 | base_dir: "/opt/coins" 13 | 14 | # is the parametr which allows to override chain data location 15 | data_dir: "{{ base_dir }}/data" 16 | 17 | chain_backend_dir: "{{ data_dir }}/{{ chain_name }}/backend" 18 | 19 | blockbook_version: "v0.3.6" 20 | 21 | blockbook_vars_file: configs/environ.json 22 | 23 | backend_vars_file: "configs/coins/{{ chain_name }}.json" 24 | 25 | backend_service: "backend-{{ chain_name }}.service" 26 | 27 | blockbook_service: "blockbook-{{ chain_name }}.service" 28 | -------------------------------------------------------------------------------- /roles/blockbook/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: vorotech 4 | description: Ansible role to manage Blockbook supporter blockchains 5 | company: Trust Wallet 6 | license: MIT 7 | min_ansible_version: 2.10.6 8 | platforms: 9 | - name: Debian 10 | versions: 11 | - all 12 | galaxy_tags: 13 | - blockchain 14 | - blockbook 15 | dependencies: [] 16 | -------------------------------------------------------------------------------- /roles/blockbook/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Install apt packages" 3 | ansible.builtin.apt: 4 | update_cache: true 5 | cache_valid_time: 86400 6 | pkg: make 7 | 8 | - name: "Git checkout repo" 9 | debug: 10 | msg: "Git checkout repo: {{ blockbook_git_repo }}" 11 | 12 | - name: "Git checkout" 13 | ansible.builtin.git: 14 | repo: "{{ blockbook_git_repo }}" 15 | dest: "{{ blockbook_checkout_dir }}" 16 | version: "{{ blockbook_git_version }}" 17 | force: true 18 | 19 | - name: "Override blockbook config" 20 | ansible.builtin.template: 21 | src: "templates/environ.json.j2" 22 | dest: "{{ blockbook_checkout_dir }}/{{ blockbook_vars_file }}" 23 | mode: "644" 24 | 25 | - name: "Ensure data directory exist" 26 | ansible.builtin.file: 27 | path: "{{ chain_backend_dir }}" 28 | state: directory 29 | 30 | - name: "Read {{ chain_name }} backend package vars" 31 | ansible.builtin.shell: "cat {{ blockbook_checkout_dir }}/{{ backend_vars_file }}" 32 | register: backend_vars_result 33 | 34 | - name: "Parse backend vars" 35 | set_fact: 36 | backend_vars: "{{ backend_vars_result.stdout | from_json }}" 37 | 38 | - name: "Check {{ chain_name }} backend package was built" 39 | ansible.builtin.stat: 40 | path: "{{ blockbook_checkout_dir }}/build/backend-{{ chain_name }}_{{ backend_vars.backend.version }}-{{ backend_vars.backend.package_revision }}_amd64.deb" 41 | register: backend_package_stat 42 | 43 | - name: "Check blockbook package was built" 44 | ansible.builtin.stat: 45 | path: "{{ blockbook_checkout_dir }}/build/blockbook-{{ chain_name }}_{{ blockbook_version | replace('v','') }}_amd64.deb" 46 | register: blockbook_package_stat 47 | 48 | - name: "Make {{ chain_name }} packages" 49 | ansible.builtin.shell: 50 | cmd: "make all-{{ chain_name }}" 51 | chdir: "{{ blockbook_checkout_dir }}" 52 | when: not backend_package_stat.stat.exists or not blockbook_package_stat.stat.exists 53 | register: make_packages 54 | 55 | - name: "Install {{ chain_name }} backend package" 56 | ansible.builtin.shell: 57 | cmd: "apt install ./backend-{{ chain_name }}_{{ backend_vars.backend.version }}-{{ backend_vars.backend.package_revision }}_amd64.deb -y --allow-downgrades" 58 | chdir: "{{ blockbook_checkout_dir }}/build" 59 | changed_when: make_packages.changed 60 | 61 | - name: "Enable and start {{ chain_name }} backend service" 62 | ansible.builtin.systemd: 63 | name: "{{ backend_service }}" 64 | state: restarted 65 | daemon_reload: true 66 | enabled: true 67 | 68 | - name: "Install {{ chain_name }} blockbook package" 69 | ansible.builtin.shell: 70 | cmd: "apt install ./blockbook-{{ chain_name }}_{{ blockbook_version | replace('v','') }}_amd64.deb -y" 71 | chdir: "{{ blockbook_checkout_dir }}/build" 72 | changed_when: make_packages.changed 73 | 74 | - name: "Enable and start {{ coin }} blockbook service" 75 | ansible.builtin.systemd: 76 | name: "{{ blockbook_service }}" 77 | state: restarted 78 | daemon_reload: true 79 | enabled: true 80 | -------------------------------------------------------------------------------- /roles/blockbook/templates/environ.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "version": "{{ blockbook_version | replace('v','') }}", 3 | "backend_install_path": "{{ base_dir }}/nodes", 4 | "backend_data_path": "{{ data_dir }}", 5 | "blockbook_install_path": "{{ base_dir }}/blockbook", 6 | "blockbook_data_path": "{{ data_dir }}" 7 | } 8 | -------------------------------------------------------------------------------- /roles/cosmos/README.md: -------------------------------------------------------------------------------- 1 | ../../docs/cosmos_role.md -------------------------------------------------------------------------------- /roles/cosmos/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # is the non-root user under which daemon will operate 3 | username: cosmos 4 | 5 | # is a user home dir 6 | home_dir: "/home/{{ username }}" 7 | 8 | # is a binaries location dir, should be in the PATH. 9 | bin_dir: /usr/local/bin 10 | 11 | # is the URL to get Cosmos-SDK based chain metadata. 12 | # This value will almost never change. 13 | chain_registry_url: https://raw.githubusercontent.com/cosmos/chain-registry/master/{{ chain_name }}/chain.json 14 | 15 | # is the 'system/architecture' string to download chain binary. 16 | # chain_distro: e.g. linux/amd64 17 | 18 | # defines the a Cosmos-SDK based chain to init 19 | # Requred to be set in role vars. 20 | chain_name: "" 21 | 22 | # is an optinal extra bin flags 23 | # e.g. extra_run_args: "--log_format json" 24 | 25 | # is an optional url to download specific version of chain binary. 26 | # Since chain registry might have outdated data or when secure patch has been released. 27 | # e.g. chain_bin_url: https://github.com/osmosis-labs/osmosis/releases/download/v6.1.0/osmosisd-6.1.0-linux-amd64 28 | 29 | # is the parametr which allows to override chain data location (--home flag). 30 | # By default equals node_home value from chain registry (e.g. /home/cosmos/.osmosisd) 31 | # e.g. data_dir: /mnt/data/.osmosisd 32 | 33 | # defines minimum transaction fee 34 | # it is recommended to set this parameter 35 | # to prevent getting spammed by transactions with no gas 36 | # e.g. chain_minimum_gas_prices: 0.25token1;0.0001token2 37 | 38 | # is an optinal URL to download chain addrbook.json 39 | # e.g. chain_addrbook_url: https://network.terra.dev/addrbook.json 40 | 41 | # is the custom username of your node 42 | chain_moniker: moniker-1 43 | 44 | # defines the pruning strategy. 45 | # Avaiable options are default, nothing, everything, custom. 46 | chain_pruning: default 47 | 48 | # defines number of last states to keep (e.g. 100). 49 | # Only applied when chain_pruning is custom. 50 | chain_pruning_keep_recent: "0" 51 | 52 | # defines the prunning interval (e.g. 10) 53 | # Only applied when chain_pruning is custom. 54 | chain_pruning_interval: "0" 55 | 56 | # defines if the API server should be enabled. 57 | chain_api_enable: true 58 | 59 | # quicksync_available defines whether quicksync mode is avaialble for chain. 60 | # Set in chain vars. 61 | quicksync_available: false 62 | 63 | # defines the chain data file to download. 64 | # Avaialable values are pruned, default, archive and none (sync from beginning). 65 | quicksync_mode: pruned 66 | 67 | # selects the regional mirror. 68 | # Avaiable options are Netherlands, Singapore, SanFrancisco 69 | quicksync_mirror: Netherlands 70 | 71 | # is the maximum number of connections to the server for each download. 72 | quicksync_connection_num: 5 73 | 74 | # forces the chain data download step. 75 | # Warning, the content of the data dir will be wiped. 76 | # By default chain data will be downloaded only after chain init. 77 | quicksync_force: false 78 | 79 | # skips steps to download quicksync chain data archive, 80 | # assuming that the file has been already downloaded. 81 | quicksync_skip_download: false 82 | 83 | # skips steps to perform the checksum over downloaded archive, 84 | # quicksync.io has issues recently with checksum integrity. 85 | quicksync_skip_checksum: false 86 | 87 | # allows to override target url to download chain data archive 88 | # e.g. quicksync_target_url: 89 | 90 | # is the temporary dir to keep downloaded file. 91 | # The aria2c package is utilized to speed up download process, 92 | # but it requires double size to first download and then unpack data archive. 93 | # In cloud environment the download dir often points to temporary mounted drive. 94 | quicksync_tmp_dir: /tmp/quicksync 95 | 96 | # is the location of the golang bin folder which will be added 97 | # to the $PATH. 98 | golang_bin_dir: /usr/local/go/bin 99 | -------------------------------------------------------------------------------- /roles/cosmos/files/checksum.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FILE=$1 3 | COMMAND=$2 4 | 5 | if [ "$FILE" == "" ] 6 | then 7 | echo "No file provided" 8 | exit 1 9 | fi 10 | if [ "$COMMAND" != "check" -a "$COMMAND" != "create" ] 11 | then 12 | echo "no create or check command specified, assuming check" 13 | COMMAND=check 14 | fi 15 | 16 | if [ ! -f ${FILE} ] 17 | then 18 | echo "no file found to checksum" 19 | exit 1 20 | fi 21 | SIZE=`du -Bg $FILE|sed 's/\(\d*\)G.*/\1/'` 22 | 23 | if [ "$COMMAND" == "create" ] 24 | then 25 | > ${FILE}.checksum 26 | for((i=1;i<=$SIZE;++i)) do 27 | dd bs=1M skip=$((1024*$i)) count=1 if=$FILE 2>/dev/null | sha512sum >> ${FILE}.checksum 28 | done 29 | echo "CHECKSUM CREATED" 30 | exit 0 31 | elif [ -f ${FILE}.checksum -a "$COMMAND" == "check" ] 32 | then 33 | for((i=1;i<=$SIZE;++i)) do 34 | CHECKSUM=`dd bs=1M skip=$((1024*$i)) count=1 if=$FILE 2>/dev/null | sha512sum |awk '{print $1}'` 35 | LINE=`grep -n $CHECKSUM ${FILE}.checksum|awk -F\: '{print $1}'` 36 | if [ "$LINE" != "$i" ] 37 | then 38 | echo "CHECKSUM FAILED" 39 | exit 1 40 | fi 41 | done 42 | echo "CHECKSUM SUCCEEDED" 43 | exit 0 44 | elif [ ! -f ${FILE}.checksum -a "$COMMAND" == "check" ] 45 | then 46 | echo "no checksum file found" 47 | exit 1 48 | fi 49 | -------------------------------------------------------------------------------- /roles/cosmos/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Restart {{ chain_name }} service" 3 | ansible.builtin.systemd: 4 | name: "{{ chain_daemon_name }}" 5 | state: restarted 6 | daemon_reload: true 7 | enabled: true 8 | listen: restart_service 9 | -------------------------------------------------------------------------------- /roles/cosmos/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: vorotech 4 | description: Ansible role to manage Cosmos-SDK based blockchain nodes 5 | company: Trust Wallet 6 | license: MIT 7 | min_ansible_version: 2.10.6 8 | platforms: 9 | - name: Ubuntu 10 | versions: 11 | - all 12 | galaxy_tags: 13 | - blockchain 14 | - cosmos 15 | - terra 16 | - osmosis 17 | dependencies: [] 18 | -------------------------------------------------------------------------------- /roles/cosmos/tasks/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Set chain configs" 3 | ansible.builtin.set_fact: 4 | chain_config_path: "{{ data_dir }}/config/config.toml" 5 | chain_config_app_path: "{{ data_dir }}/config/app.toml" 6 | 7 | # config.toml 8 | 9 | - name: "Update persistent peers" # noqa risky-file-permissions 10 | community.general.ini_file: 11 | path: "{{ chain_config_path }}" 12 | section: "p2p" 13 | option: "persistent_peers" 14 | value: "\"{{ chain_peers }}\"" 15 | notify: restart_service 16 | 17 | - name: "Update indexer" # noqa risky-file-permissions 18 | community.general.ini_file: 19 | path: "{{ chain_config_path }}" 20 | section: "tx_index" 21 | option: "indexer" 22 | value: "\"kv\"" 23 | when: chain_pruning != 'everything' 24 | notify: restart_service 25 | 26 | - name: "Update indexer (if pruning is everyting)" # noqa risky-file-permissions 27 | community.general.ini_file: 28 | path: "{{ chain_config_path }}" 29 | section: "tx_index" 30 | option: "indexer" 31 | value: "\"null\"" 32 | when: chain_pruning == 'everything' 33 | notify: restart_service 34 | 35 | # app.toml 36 | 37 | - name: "Update minimum gas price" # noqa risky-file-permissions 38 | community.general.ini_file: 39 | path: "{{ chain_config_app_path }}" 40 | section: "" 41 | option: "minimum-gas-prices" 42 | value: "\"{{ chain_minimum_gas_prices }}\"" 43 | when: chain_minimum_gas_prices is defined 44 | notify: restart_service 45 | 46 | - name: "Update pruning strategy" # noqa risky-file-permissions 47 | community.general.ini_file: 48 | path: "{{ chain_config_app_path }}" 49 | section: "{{ item.section }}" 50 | option: "{{ item.option }}" 51 | value: "{{ item.value }}" 52 | loop: 53 | - {section: "", option: pruning, value: "\"{{ chain_pruning }}\""} 54 | - {section: "", option: pruning-keep-recent, value: "\"{{ chain_pruning_keep_recent }}\""} 55 | - {section: "", option: pruning-interval, value: "\"{{ chain_pruning_interval }}\""} 56 | notify: restart_service 57 | 58 | - name: "Enable API server" # noqa risky-file-permissions 59 | community.general.ini_file: 60 | path: "{{ chain_config_app_path }}" 61 | section: "api" 62 | option: "enable" 63 | value: "{{ chain_api_enable | string | lower }}" 64 | notify: restart_service 65 | 66 | - name: "Include chain tasks only if such exist, otherwise skip the task" 67 | ansible.builtin.include_tasks: 68 | file: "{{ item }}" 69 | with_first_found: 70 | - files: 71 | - "config_{{ chain_name }}.yml" 72 | skip: true 73 | -------------------------------------------------------------------------------- /roles/cosmos/tasks/config_osmosis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Disable snapshot-interval" # noqa risky-file-permissions 3 | community.general.ini_file: 4 | path: "{{ chain_config_app_path }}" 5 | section: "state-sync" 6 | option: "snapshot-interval" 7 | value: 0 8 | -------------------------------------------------------------------------------- /roles/cosmos/tasks/init.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://stackoverflow.com/a/56379678/166473 3 | - name: "Install acl package" 4 | ansible.builtin.apt: 5 | update_cache: true 6 | cache_valid_time: 86400 7 | pkg: acl 8 | 9 | # Only init once when there is no genesis.json file 10 | - name: "Init {{ chain_name }} chain" 11 | ansible.builtin.command: "{{ chain_bin }} init {{ chain_moniker }} --home={{ data_dir }} --chain-id={{ chain_id }}" 12 | args: 13 | creates: "{{ data_dir }}/config/genesis.json" 14 | become: true 15 | become_user: "{{ username }}" 16 | register: init_result 17 | notify: restart_service 18 | 19 | - name: "Download the genesis file (instead default)" # noqa no-handler 20 | ansible.builtin.get_url: 21 | url: "{{ chain_genesis_url }}" 22 | dest: "{{ data_dir }}/config/genesis.json" 23 | owner: "{{ username }}" 24 | force: true 25 | mode: "640" 26 | when: init_result.changed 27 | 28 | - name: "Download addrbook file" # noqa no-handler 29 | ansible.builtin.get_url: 30 | url: "{{ chain_addrbook_url }}" 31 | owner: "{{ username }}" 32 | dest: "{{ data_dir }}/config/addrbook.json" 33 | mode: "640" 34 | when: 35 | - init_result.changed 36 | - chain_addrbook_url is defined 37 | notify: restart_service 38 | -------------------------------------------------------------------------------- /roles/cosmos/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Create user {{ username }}" 3 | ansible.builtin.user: 4 | name: "{{ username }}" 5 | 6 | - name: "Create systemd service file" 7 | ansible.builtin.template: 8 | src: templates/service.j2 9 | dest: "/etc/systemd/system/{{ chain_daemon_name }}.service" 10 | mode: "644" 11 | 12 | - name: "Get {{ chain_bin }} binary version" # noqa no-changed-when 13 | ansible.builtin.shell: 14 | cmd: "{{ bin_dir }}/{{ chain_bin }} version 2>&1" 15 | failed_when: false 16 | register: chain_version_result 17 | 18 | - name: "Download chain binary" 19 | when: 20 | - chain_version is defined 21 | - chain_version_result.stdout is not search(chain_version | replace('v', '')) 22 | - chain_bin_url is defined 23 | - chain_git_repo is not defined 24 | block: 25 | - name: "Stop service (if running)" 26 | ansible.builtin.systemd: 27 | name: "{{ chain_daemon_name }}" 28 | state: stopped 29 | 30 | - name: "Remove previous file" 31 | ansible.builtin.file: 32 | dest: "{{ bin_dir }}/{{ chain_bin }}" 33 | state: absent 34 | 35 | - name: "Unarchive chain binary" 36 | ansible.builtin.unarchive: 37 | src: "{{ chain_bin_url }}" 38 | dest: "{{ bin_dir }}" 39 | mode: "755" 40 | remote_src: true 41 | when: chain_bin_url is regex('\.tar\.gz$') # .tar.gz 42 | notify: restart_service 43 | 44 | - name: "Download {{ chain_name }} binary" 45 | ansible.builtin.get_url: 46 | url: "{{ chain_bin_url }}" 47 | dest: "{{ bin_dir }}" 48 | mode: "755" 49 | register: get_url_result 50 | when: chain_bin_url is regex('[_-][^.]+$') # no extension, e.g. osmosisd-6.1.0-linux-amd64 51 | notify: restart_service 52 | 53 | - name: "Link download chain binary" 54 | ansible.builtin.file: 55 | src: "{{ get_url_result.dest }}" 56 | dest: "{{ bin_dir }}/{{ chain_bin }}" 57 | state: link 58 | when: chain_bin_url is regex('[_-][^.]+$') # no extension, e.g. osmosisd-6.1.0-linux-amd64 59 | notify: restart_service 60 | 61 | - name: "Make chain binary" 62 | when: 63 | - chain_version is defined 64 | - chain_version_result.stdout is not search(chain_version | replace('v', '')) 65 | - chain_git_repo is defined 66 | block: 67 | - name: "Stop service (if running)" 68 | ansible.builtin.systemd: 69 | name: "{{ chain_daemon_name }}" 70 | state: stopped 71 | 72 | - name: "Remove previous file" 73 | ansible.builtin.file: 74 | dest: "{{ bin_dir }}/{{ chain_bin }}" 75 | state: absent 76 | 77 | - name: "Install apt packages" 78 | ansible.builtin.apt: 79 | update_cache: true 80 | cache_valid_time: 86400 81 | pkg: 82 | - build-essential 83 | - curl 84 | - git 85 | - jq 86 | - snapd 87 | - ufw 88 | 89 | - name: "Git checkout chain repo" 90 | ansible.builtin.git: 91 | repo: "{{ chain_git_repo }}" 92 | dest: "/tmp/{{ chain_name }}" 93 | version: "{{ chain_version }}" 94 | 95 | - name: "Make {{ chain_name }} binary" # noqa no-changed-when 96 | ansible.builtin.shell: "{{ make_command | default('make install') }}" 97 | args: 98 | chdir: "/tmp/{{ chain_name }}" 99 | environment: 100 | PATH: "{{ golang_bin_dir }}:{{ ansible_env.PATH }}" 101 | GOBIN: "{{ bin_dir }}" 102 | notify: restart_service 103 | 104 | - name: "Ensure permissions of data dir" 105 | ansible.builtin.file: 106 | dest: "{{ data_dir }}" 107 | state: directory 108 | owner: "{{ username }}" 109 | mode: "774" 110 | -------------------------------------------------------------------------------- /roles/cosmos/tasks/load_vars.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Include {{ chain_name }} vars" 3 | ansible.builtin.include_vars: 4 | file: "vars/{{ chain_name }}.yml" 5 | 6 | - name: "Get metadata from chain registry" 7 | ansible.builtin.uri: 8 | url: "{{ chain_registry_url }}" 9 | return_content: true 10 | status_code: 11 | - 200 12 | register: metadata 13 | 14 | - name: "Set amd64 instead of x86_64" 15 | ansible.builtin.set_fact: 16 | system_architecture: amd64 17 | when: ansible_architecture == 'x86_64' 18 | 19 | - name: "Set chain distro" 20 | ansible.builtin.set_fact: 21 | chain_distro: "{{ chain_distro | default(ansible_system~'/'~system_architecture | default(ansible_architecture)) | lower }}" 22 | 23 | - name: "Set role facts" 24 | ansible.builtin.set_fact: 25 | extra_run_args: "{{ extra_run_args | default('') }}" 26 | chain_bin: "{{ metadata.json.daemon_name }}" 27 | data_dir: "{{ data_dir | default(metadata.json.node_home | replace('$HOME', home_dir) ) }}" 28 | chain_genesis_url: "{{ metadata.json.codebase.genesis.genesis_url }}" 29 | chain_id: "{{ metadata.json.chain_id }}" 30 | chain_peers: "{{ metadata.json.peers.persistent_peers | trustwallet.blockchain.to_cosmos_peers }}" 31 | chain_daemon_name: "{{ metadata.json.daemon_name }}" 32 | 33 | - name: "Set chain bin url" 34 | ansible.builtin.set_fact: 35 | chain_bin_url: "{{ metadata.json.codebase.binaries[chain_distro] }}" 36 | when: 37 | - chain_bin_url is not defined 38 | - metadata.json.codebase.binaries | default([]) | length > 0 39 | - metadata.json.codebase.binaries[chain_distro] is defined 40 | -------------------------------------------------------------------------------- /roles/cosmos/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: load_vars.yml 3 | 4 | - include_tasks: install.yml 5 | 6 | - include_tasks: init.yml 7 | 8 | - include_tasks: config.yml 9 | 10 | - include_tasks: sync.yml 11 | when: 12 | - init_result.changed or quicksync_force 13 | - quicksync_available 14 | - quicksync_mode != 'none' 15 | -------------------------------------------------------------------------------- /roles/cosmos/tasks/sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Install apt packages" 3 | ansible.builtin.apt: 4 | update_cache: true 5 | cache_valid_time: 86400 6 | pkg: 7 | - aria2 8 | - jq 9 | - liblz4-tool 10 | 11 | - name: "Get download target" # noqa command-instead-of-module 12 | ansible.builtin.shell: 13 | cmd: > 14 | curl -L https://quicksync.io/{{ chain_name }}.json 15 | | jq -r '.[]|select(.file=="{{ chain_id }}-{{ quicksync_mode }}")|select (.mirror=="{{ quicksync_mirror }}")' 16 | changed_when: false 17 | register: quicksync_target_result 18 | 19 | - name: "Set quicksync facts" 20 | ansible.builtin.set_fact: 21 | quicksync_target: "{{ quicksync_target_result.stdout | from_json }}" 22 | 23 | - name: "Set target url and filename" 24 | ansible.builtin.set_fact: 25 | target_url: "{{ quicksync_target_url | default(quicksync_target.url) }}" 26 | target_filename: "{{ quicksync_target.filename }}" 27 | 28 | - name: "Ensure download dir permissions" 29 | ansible.builtin.file: 30 | dest: "{{ quicksync_tmp_dir }}" 31 | state: directory 32 | mode: "777" 33 | 34 | - name: "Find downloaded data archive (if skip download)" 35 | ansible.builtin.find: 36 | paths: "{{ quicksync_tmp_dir }}" 37 | patterns: "*.tar.gz" 38 | register: local_data 39 | when: quicksync_skip_download 40 | 41 | - name: "Override target filename (if skip download)" 42 | ansible.builtin.set_fact: 43 | target_filename: "{{ local_data.files[0] }}" 44 | when: 45 | - quicksync_skip_download 46 | - local_data.files | length > 0 47 | 48 | - name: "Download chain data archive" 49 | ansible.builtin.shell: 50 | cmd: "aria2c --continue --max-connection-per-server={{ quicksync_connection_num }} {{ target_url }} > aria.log" 51 | chdir: "{{ quicksync_tmp_dir }}" 52 | async: 72000 53 | poll: 10 54 | when: not quicksync_skip_download 55 | 56 | - name: "Download checksum file" 57 | ansible.builtin.get_url: 58 | url: "{{ target_url }}.checksum" 59 | dest: "{{ quicksync_tmp_dir }}" 60 | mode: "755" 61 | when: 62 | - not quicksync_skip_download 63 | - not quicksync_skip_checksum 64 | 65 | - name: "Copy checksum tool" 66 | ansible.builtin.copy: 67 | src: checksum.sh 68 | dest: "{{ quicksync_tmp_dir }}" 69 | mode: "755" 70 | when: 71 | - not quicksync_skip_download 72 | - not quicksync_skip_checksum 73 | 74 | - name: "Checksum downloaded archive" 75 | ansible.builtin.command: 76 | cmd: "./checksum.sh {{ target_filename }}" 77 | chdir: "{{ quicksync_tmp_dir }}" 78 | when: 79 | - not quicksync_skip_download 80 | - not quicksync_skip_checksum 81 | 82 | - name: "Stop service (if running)" 83 | ansible.builtin.systemd: 84 | name: "{{ chain_daemon_name }}" 85 | state: stopped 86 | 87 | - name: "Delete inner data dir" 88 | ansible.builtin.file: 89 | path: "{{ data_dir }}/data" 90 | state: absent 91 | notify: restart_service 92 | 93 | - name: "Extract chain data" # noqa command-instead-of-module 94 | ansible.builtin.command: 95 | cmd: "tar -I lz4 -xf {{ quicksync_tmp_dir }}/{{ target_filename }}" 96 | chdir: "{{ data_dir }}" 97 | async: 72000 98 | poll: 10 99 | become: true 100 | become_user: "{{ username }}" 101 | changed_when: false 102 | -------------------------------------------------------------------------------- /roles/cosmos/templates/service.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | [Unit] 4 | Description={{ chain_name }} Daemon 5 | After=network.target 6 | 7 | [Service] 8 | Type=simple 9 | User={{ username }} 10 | ExecStart={{ bin_dir }}/{{ chain_bin }} start --home {{ data_dir }} {{ extra_run_args }} {{ chain_id }} 11 | Restart=on-failure 12 | RestartSec=3 13 | LimitNOFILE=4096 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /roles/cosmos/vars/osmosis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | chain_addrbook_url: https://quicksync.io/addrbook.osmosis.json 3 | quicksync_available: true 4 | chain_version: v15.0.0 5 | chain_bin_url: https://github.com/osmosis-labs/osmosis/releases/download/v15.0.0/osmosisd-15.0.0-linux-amd64 6 | -------------------------------------------------------------------------------- /roles/cosmos/vars/terra.yml: -------------------------------------------------------------------------------- 1 | --- 2 | chain_bin_flags: "--x-crisis-skip-assert-invariants" 3 | chain_minimum_gas_prices: "0.01133uluna,0.15uusd,0.104938usdr,169.77ukrw,428.571umnt,0.125ueur,0.98ucny,16.37ujpy,0.11ugbp,10.88uinr,0.19ucad,0.14uchf,0.19uaud,0.2usgd,4.62uthb,1.25usek,1.25unok,0.9udkk,2180.0uidr,7.6uphp,1.17uhkd" 4 | chain_addrbook_url: https://quicksync.io/addrbook.terra.json 5 | quicksync_available: true 6 | -------------------------------------------------------------------------------- /roles/ethereum/README.md: -------------------------------------------------------------------------------- 1 | ../../docs/ethereum_role.md -------------------------------------------------------------------------------- /roles/ethereum/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # is the non-root user under which daemon will operate 3 | username: geth 4 | 5 | # is a user home dir 6 | home_dir: "/home/{{ username }}" 7 | 8 | # defines the chain to init 9 | chain_name: ethereum 10 | 11 | # is the chain executable 12 | chain_bin: geth 13 | 14 | # is an optinal extra flags 15 | extra_run_args: "" 16 | 17 | # is the parametr which allows to override chain data location 18 | data_dir: "{{ home_dir }}/.ethereum" 19 | 20 | # is the name of the SystemD service file 21 | geth_daemon_name: gethd 22 | 23 | # is the SystemD unit description 24 | daemon_description: Ethereum Chain Daemon 25 | 26 | geth_config: {} 27 | 28 | default_geth_config: 29 | Eth: 30 | # full: Downloads all blocks (including headers, transactions, and receipts) and generates the state of the blockchain incrementally by executing every block. 31 | # fast: Downloads all blocks (including headers, transactions and receipts), verifies all headers, and downloads the state and verifies it against the headers. 32 | # snap: Same functionality as fast, but with a faster algorithm. 33 | # light: Downloads all block headers, block data, and verifies some randomly. 34 | SyncMode: "snap" 35 | Node: 36 | DataDir: "{{ data_dir }}" 37 | HTTPHost: "0.0.0.0" 38 | HTTPPort: 8545 39 | HTTPVirtualHosts: 40 | - "*" 41 | HTTPModules: 42 | - eth 43 | - net 44 | - web3 45 | Metrics: 46 | Enabled: false 47 | HTTP: "0.0.0.0" 48 | Port: 6060 49 | -------------------------------------------------------------------------------- /roles/ethereum/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Restart Geth service" 3 | ansible.builtin.systemd: 4 | name: "{{ geth_daemon_name }}" 5 | state: restarted 6 | daemon_reload: true 7 | enabled: true 8 | listen: restart_service 9 | -------------------------------------------------------------------------------- /roles/ethereum/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: vorotech 4 | description: Ansible role to manage Ethereum blockchain node with Geth 5 | company: Trust Wallet 6 | license: MIT 7 | min_ansible_version: 2.10.6 8 | platforms: 9 | - name: Ubuntu 10 | versions: 11 | - all 12 | galaxy_tags: 13 | - blockchain 14 | - ethereum 15 | - geth 16 | dependencies: [] 17 | -------------------------------------------------------------------------------- /roles/ethereum/tasks/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Override data_dir from specified config" 3 | ansible.builtin.set_fact: 4 | data_dir: "{{ geth_config.Node.DataDir }}" 5 | when: 6 | - geth_config is defined 7 | - geth_config.Node is defined 8 | - geth_config.Node.DataDir is defined 9 | 10 | - name: "Ensure data dir exists" 11 | ansible.builtin.file: 12 | dest: "{{ data_dir }}" 13 | state: directory 14 | owner: "{{ username }}" 15 | mode: "774" 16 | 17 | - name: "geth - Merge default and custom config" 18 | ansible.builtin.set_fact: 19 | final_geth_config: "{{ default_geth_config | combine(geth_config, recursive=true) }}" 20 | 21 | - name: "geth - Copy final config in place" 22 | ansible.builtin.template: 23 | src: config.toml.j2 24 | dest: "{{ data_dir }}/config.toml" 25 | owner: "{{ username }}" 26 | mode: "644" 27 | when: 28 | - final_geth_config is defined 29 | - final_geth_config.keys() | length > 0 30 | notify: restart_service 31 | -------------------------------------------------------------------------------- /roles/ethereum/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Create user {{ username }}" 3 | ansible.builtin.user: 4 | name: "{{ username }}" 5 | 6 | - name: "Create systemd service file" 7 | ansible.builtin.template: 8 | src: templates/service.j2 9 | dest: "/etc/systemd/system/{{ geth_daemon_name }}.service" 10 | mode: "644" 11 | notify: restart_service 12 | 13 | - name: "Install repository from PPA" 14 | ansible.builtin.apt_repository: 15 | repo: ppa:ethereum/ethereum 16 | when: 17 | - ansible_distribution == "Ubuntu" 18 | 19 | - name: "Install apt package" 20 | ansible.builtin.apt: 21 | update_cache: true 22 | cache_valid_time: 86400 23 | pkg: ethereum 24 | when: 25 | - ansible_distribution == "Ubuntu" 26 | notify: restart_service 27 | -------------------------------------------------------------------------------- /roles/ethereum/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: install.yml 3 | 4 | - include_tasks: config.yml 5 | -------------------------------------------------------------------------------- /roles/ethereum/templates/config.toml.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | # see: Geth configuration logic -- https://github.com/ethereum/go-ethereum/blob/master/cmd/geth/config.go 3 | 4 | {% for section in final_geth_config %} 5 | [{{ section }}] 6 | {% for key, value in final_geth_config[section].items() %} 7 | {% if value is string %} 8 | {{ key }} = "{{ value }}" 9 | {% else %} 10 | {{ key }} = {{ value | to_json }} 11 | {% endif %} 12 | {% endfor %} 13 | 14 | {% endfor %} 15 | -------------------------------------------------------------------------------- /roles/ethereum/templates/service.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | [Unit] 4 | Description={{ daemon_description }} 5 | After=network.target 6 | 7 | [Service] 8 | Type=simple 9 | User={{ username }} 10 | ExecStart={{ chain_bin }} --config {{ data_dir }}/config.toml {{ extra_run_args }} 11 | Restart=on-failure 12 | RestartSec=3 13 | LimitNOFILE=65535 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /roles/ethereum/tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | -------------------------------------------------------------------------------- /roles/ethereum/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - roles/ethereum 6 | -------------------------------------------------------------------------------- /roles/nano/README.md: -------------------------------------------------------------------------------- 1 | # Nano Role 2 | 3 | Ansible role to manage Nano blockchain node. 4 | 5 | ## Role Variables 6 | 7 | The role has default variables (see `defaults/main.yml`) which can be adjusted. 8 | 9 | ## Example Playbook 10 | 11 | ```yaml 12 | - hosts: "all" 13 | gather_facts: true 14 | 15 | roles: 16 | - role: geerlingguy.docker # for nano_node 17 | become: true 18 | - role: gantsign.golang # for nano_rpc_cache 19 | golang_version: "1.18.1" 20 | golang_install_dir: /usr/local/go 21 | - role: nvidia.nvidia_driver # for nano-work-server 22 | become: true 23 | 24 | - role: trustwallet.blockchain.nano 25 | data_dir: /mnt/data/nano # exampe of non-default data directory, default is /home/nano/.nano 26 | nano_node: true # default, could be omitted 27 | nano_work_server: true # default, could be omitted 28 | nano_work_server_peers: # default, could be omitted 29 | - http://localhost:7776 30 | nano_work_server: true # default, Work Generation needs a machine with GPU attached 31 | ``` 32 | 33 | Nano documentation suggests to split RPC node and Work Generation to separate machines, but single setup machine still valid. 34 | 35 | Alternative setup with two machines: 36 | 37 | ```yaml 38 | - hosts: "nano_node" 39 | gather_facts: true 40 | 41 | roles: 42 | - role: geerlingguy.docker # for nano_node 43 | become: true 44 | - role: gantsign.golang # for nano_rpc_cache 45 | golang_version: "1.18.1" 46 | golang_install_dir: /usr/local/go 47 | 48 | - role: trustwallet.blockchain.nano 49 | data_dir: /mnt/data/.nano # exampe of non-default data directory, default is /home/nano/.nano 50 | nano_node: true 51 | nano_work_server: false 52 | nano_work_server_peers: # note: only a single worker server peer is supported atm 53 | - https://:7776 54 | 55 | - hosts: "nano_work_server" 56 | gather_facts: true 57 | 58 | roles: 59 | - role: nvidia.nvidia_driver # for nano-work-server 60 | become: true 61 | 62 | - role: trustwallet.blockchain.nano 63 | nano_node: false 64 | nano_work_server: true 65 | ``` 66 | 67 | ## Popular Questions 68 | 69 | ### Hardware Recommendations 70 | 71 | Non-voting and Representative Nodes: 72 | 73 | * 8GB RAM 74 | * Quad-Core CPU 75 | * 250 Mbps bandwidth (4TB or more of available monthly bandwidth) 76 | * SSD-based hard drive with 400GB+ of free space 77 | 78 | For nodes being used with services requiring regular or high volume sending and receiving of transactions, 79 | special considerations must be made for handling Proof-of-Work generation activities. 80 | See [Work Generation](https://docs.nano.org/integration-guides/work-generation/) guidance. 81 | 82 | NOTE: ARM based platforms is not yet supported for Nano Work Server. 83 | 84 | NOTE: This role doesn't install any GPU drivers, should be installed separately (examples shows the NVIDIA driver installation). 85 | 86 | ### Port Configuration 87 | 88 | * `7075` - Nano P2P port for the node to participate on the network, should be open to the world `0.0.0.0/0`. 89 | 90 | * `7076` - Nano RPC access port, should only be available to those you wish to have control of the node, since option to `enable_control` is set to on. 91 | 92 | * `7376` - [Nano RPC Cache](https://github.com/catenocrypt/nano-work-cache) is an additional caching layer (not official), which simplifies interaction for 93 | light wallets which need to perform work generation on the server side, 94 | provides cache for some actions, and blocks potentially risky actions from being invoked outside of the local peers, should be open to the world `0.0.0.0/0`. 95 | 96 | * `7176` - Nano Worker Server port, so Nano PRC can request work generation. 97 | 98 | ## Troubleshooting 99 | 100 | ### nano-work-server – Unable to get platform id list after 10 seconds of waiting. 101 | 102 |  🔰 **Ensure drivers are installed correctly** 103 | 104 | On Linux you can check with the `sudo lshw -C display` command. 105 | 106 |
107 | Example output 108 | 109 | ```sh 110 | $ sudo lshw -C display 111 | *-display:0 UNCLAIMED 112 | description: VGA compatible controller 113 | product: GD 5446 114 | vendor: Cirrus Logic 115 | physical id: 2 116 | bus info: pci@0000:00:02.0 117 | version: 00 118 | width: 32 bits 119 | clock: 33MHz 120 | capabilities: vga_controller 121 | configuration: latency=0 122 | resources: memory:e8000000-e9ffffff memory:ee080000-ee080fff memory:c0000-dffff 123 | *-display:1 124 | description: VGA compatible controller 125 | product: GK104GL [GRID K520] 126 | vendor: NVIDIA Corporation 127 | physical id: 3 128 | bus info: pci@0000:00:03.0 129 | version: a1 130 | width: 64 bits 131 | clock: 33MHz 132 | capabilities: pm msi pciexpress vga_controller bus_master cap_list rom 133 | configuration: driver=nvidia latency=248 134 | resources: irq:114 memory:ec000000-ecffffff memory:e0000000-e7ffffff memory:ea000000-ebffffff ioport:c100(size=128) memory:ee000000-ee07ffff 135 | ``` 136 |
137 | 138 | Should be no `UNCLAIMED` next to the target GPU device (in the example NVIDIA GRID K520 card has drivers installed correctly). 139 | 140 | 🔰 **Which type of driver do I need?** 141 | 142 | It really depends on the system specification and goes far beyond this Ansible role. But in our tests and working environment we are running on AWS `g5.2xlarge` GPU Accelerated instances with NVIDIA GRID K520 GPUs. The working driver for this graphics card can be installed with Ansible role: 143 | 144 | ```yml 145 | roles: 146 | - role: nvidia.nvidia_driver 147 | nvidia_driver_branch: 470 # not the latest, since latest 510 branch not working 148 | ``` 149 | 150 | 🔰 **Driver is fine, but doesn't work** 151 | 152 | As it says in [nano-work-server](https://github.com/nanocurrency/nano-work-server) project, if the OpenCL library cannot be found in the PATH, it may be necessary to link against explicitly. It can be done by passing the `opencl_lib_path` variable. 153 | 154 | ```yml 155 | roles: 156 | - role: trustwallet.blockchain.nano 157 | opencl_lib_path: "/path/to/opencl.lib" 158 | ``` 159 | 160 | 🔰 **ARM based platform? Not yet supported for Nano Work Server** 161 | 162 | There is an open [issue](https://github.com/nanocurrency/nano-work-server/issues/33) at the project GitHub. 163 | 164 | ## Development 165 | 166 | To aid in the development and testing of the Ansible role, we are 167 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 168 | 169 | ```shell 170 | molecule -v test -s nano 171 | ``` 172 | 173 | ## References 174 | 175 | * [Trust Wallet](https://trustwallet.com) 176 | * [Nano GitHub](https://github.com/nanocurrency/nano-node) 177 | * [Nano Documentation](https://docs.nano.org/running-a-node/overview/) 178 | * [Nano RPC commands](https://docs.nano.org/commands/rpc-protocol/) 179 | * [Nano Troubleshooting](https://docs.nano.org/running-a-node/troubleshooting/) 180 | * [Nano RPC Cache](https://github.com/catenocrypt/nano-work-cache) 181 | * [Nano Work Server](https://github.com/nanocurrency/nano-work-server) 182 | 183 | ## License 184 | 185 | MIT 186 | -------------------------------------------------------------------------------- /roles/nano/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # is the non-root user under which daemon will operate 3 | username: nano 4 | 5 | # is a user home dir 6 | home_dir: "/home/{{ username }}" 7 | 8 | # is a binaries location dir, should be in the PATH. 9 | bin_dir: /usr/local/bin 10 | 11 | # is the near binary version. When doesn't match, will rebuild binary from sources. 12 | nano_version: V24.0 13 | 14 | # is the near data dir 15 | data_dir: "{{ home_dir }}/.nano" 16 | 17 | # indicates that the machine is Nano RPC node handling the requests. 18 | nano_node: true 19 | 20 | # is the Nano node RPC URL used by RPC Cache layer. 21 | nano_node_rpc_url: http://localhost:7076 22 | 23 | # is an option to enable unsecure control of Nano RPC node, 24 | # but since before the Nano RPC there is a Nano RPC Cache (and only its port should be open), 25 | # it ignores unsafe action (e.g. shutdown). 26 | nano_node_rpc_enable_control: false 27 | 28 | # are the Nano Work Server peer addresses, by default targeting same machine. 29 | nano_work_server_peers: 30 | - localhost:7176 31 | 32 | # indicates the mahine is Nano Work Server performing the Prof of Work generation. 33 | nano_work_server: true 34 | 35 | # is the Nano work server version 36 | nano_work_server_version: V0.3.0 37 | 38 | nano_work_server_listen_address: "[::0]:7176" 39 | 40 | nano_work_server_git_repo: https://github.com/nanocurrency/nano-work-server.git 41 | 42 | # is the name of the SystemD service. 43 | nano_work_server_daemon_name: nano-work-server 44 | 45 | # is name of the compiled binary 46 | nano_work_server_bin: nano-work-server 47 | 48 | # indicates to set up additional caching layer which simplifies 49 | # for light wallets to generate PoW on server side and acts as a security layer 50 | # to skip certain unsafe actions when `enable_control` 51 | nano_rpc_cache: true 52 | 53 | nano_rpc_cache_git_repo: https://github.com/catenocrypt/nano-work-cache.git 54 | 55 | nano_rpc_cache_config_path: "{{ home_dir }}/nano-rpc-cache/config.toml" 56 | 57 | # With current implementation of nano-rpc-cache 58 | # only a single work server is supported 59 | nano_rpc_cache_work_server_url: http://localhost:7176 60 | 61 | # is name of the compiled binary 62 | nano_rpc_cache_bin: nano_rpc_cache 63 | 64 | nano_rpc_cache_make_command: cd ./src && go mod tidy && go build -ldflags="-s -w" -buildvcs=false -o {{ bin_dir }}/{{ nano_rpc_cache_bin }} ./main 65 | 66 | # is the name of the SystemD service. 67 | nano_rpc_cache_daemon_name: nano-rpc-cache 68 | 69 | # is the Nano RPC Cache listening port 70 | nano_rpc_cache_port: 7376 71 | 72 | # is the name of the cache file, can be relative, 73 | # can be absolute or can be empty "" to disable cache persistance to file. 74 | nano_rpc_cache_filename: "{{ home_dir }}/nano-rpc-cache/.cache" 75 | 76 | # is the number of max concurrent 77 | # outstanding (not work_generation) requests. 78 | nano_rpc_cache_max_active_requests: 1000 79 | 80 | # is the number of background worker threads, 81 | # and also maximum number of outgoing requests from pregenerations 82 | nano_rpc_cache_worker_count: 4 83 | 84 | # is the number of concurrent outgoing 85 | # work generation requests, 0 – means no limit. If set must be 86 | # at least 1 larger than nano_rpc_cache_worker_count. 87 | nano_rpc_cache_max_active_work_generation_requests: 8 88 | 89 | # indicates whether to generate PoW in advance when e.g. balance is retrieved 90 | # Warning: the settings might highly impact your system performance in case when many clients connected. 91 | nano_rpc_cache_pregeneration_enabled: false 92 | 93 | # is the maximum number of pregeneration requests to queue. 94 | nano_rpc_cache_pregeneration_queue_size: 1000 95 | 96 | # is the number of days until cached entries got cleaned up. 97 | # 0 – means no cache aging. 98 | nano_rpc_cache_age_limit_in_days: 30 99 | 100 | # is the location of the golang bin folder which will be added 101 | # to the $PATH. 102 | golang_bin_dir: /usr/local/go/bin 103 | 104 | # is the Rust toolchain version 105 | rust_version: 1.63.0 106 | -------------------------------------------------------------------------------- /roles/nano/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Restart {{ nano_rpc_cache_daemon_name }} service" 3 | ansible.builtin.systemd: 4 | name: "{{ nano_rpc_cache_daemon_name }}" 5 | state: restarted 6 | daemon_reload: true 7 | enabled: true 8 | listen: restart_nano_rpc_cache_service 9 | become: true 10 | 11 | - name: "Restart {{ nano_work_server_daemon_name }} service" 12 | ansible.builtin.systemd: 13 | name: "{{ nano_work_server_daemon_name }}" 14 | state: restarted 15 | daemon_reload: true 16 | enabled: true 17 | listen: restart_nano_work_server_service 18 | become: true 19 | -------------------------------------------------------------------------------- /roles/nano/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: vorotech 4 | description: Ansible role to manage Nano blockchain node 5 | company: Trust Wallet 6 | license: MIT 7 | min_ansible_version: 2.10.6 8 | platforms: 9 | - name: Ubuntu 10 | versions: 11 | - all 12 | galaxy_tags: 13 | - blockchain 14 | - nano 15 | dependencies: [] 16 | -------------------------------------------------------------------------------- /roles/nano/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Install apt packages" 3 | ansible.builtin.apt: 4 | pkg: acl 5 | become: true 6 | 7 | - name: "Create user {{ username }}" 8 | ansible.builtin.user: 9 | name: "{{ username }}" 10 | become: true 11 | -------------------------------------------------------------------------------- /roles/nano/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: install.yml 3 | - include_tasks: nano_node.yml 4 | when: nano_node 5 | - include_tasks: nano_rpc_cache.yml 6 | when: nano_rpc_cache 7 | - include_tasks: nano_work_server.yml 8 | when: nano_work_server 9 | -------------------------------------------------------------------------------- /roles/nano/tasks/nano_node.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Install apt packages" 3 | ansible.builtin.apt: 4 | pkg: python3-setuptools 5 | become: true 6 | 7 | - name: "Install pip packages" 8 | ansible.builtin.pip: 9 | name: docker-compose 10 | become: true 11 | 12 | - name: "Create host directory" 13 | ansible.builtin.file: 14 | path: "{{ data_dir }}/Nano" 15 | state: directory 16 | mode: "775" 17 | owner: "{{ username }}" 18 | become: true 19 | 20 | - name: "Create external volume" 21 | community.docker.docker_volume: 22 | name: nano-storage 23 | driver_options: 24 | type: none 25 | o: bind 26 | device: "{{ data_dir }}" 27 | become: true 28 | 29 | - name: "Generate Nano configs" 30 | ansible.builtin.template: 31 | src: "templates/nano-node/{{ item }}" 32 | dest: "{{ data_dir }}/Nano/{{ item | replace('.j2', '') }}" 33 | mode: "775" 34 | owner: "{{ username }}" 35 | loop: 36 | - config-node.toml.j2 37 | - config-rpc.toml.j2 38 | become: true 39 | 40 | - name: "Check docker-compose.yml exists" 41 | ansible.builtin.stat: 42 | path: "{{ home_dir }}/docker-compose.yml" 43 | register: docker_compose_check 44 | 45 | - name: "Stop service with Docker Compose" 46 | community.docker.docker_compose: 47 | project_src: "{{ home_dir }}" 48 | stopped: true 49 | timeout: 30 50 | become: true 51 | when: docker_compose_check.stat.exists 52 | ignore_errors: true 53 | 54 | - name: "Generate docker-compose.yml" 55 | ansible.builtin.template: 56 | src: templates/docker-compose.yml.j2 57 | dest: "{{ home_dir }}/docker-compose.yml" 58 | mode: "775" 59 | become: true 60 | become_user: "{{ username }}" 61 | 62 | - name: "Start services with Docker Compose" 63 | community.docker.docker_compose: 64 | project_src: "{{ home_dir }}" 65 | pull: true 66 | restarted: true 67 | become: true 68 | -------------------------------------------------------------------------------- /roles/nano/tasks/nano_rpc_cache.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Ensures Nano RPC Cache config dir exists" 3 | ansible.builtin.file: 4 | path: "{{ nano_rpc_cache_config_path | dirname }}" 5 | state: directory 6 | owner: "{{ username }}" 7 | mode: "774" 8 | become: true 9 | 10 | - name: "Generate Nano RPC Cache config" 11 | template: 12 | src: templates/nano-rpc-cache/config.toml.j2 13 | dest: "{{ nano_rpc_cache_config_path }}" 14 | owner: "{{ username }}" 15 | become: true 16 | 17 | - name: "Create systemd service file" 18 | ansible.builtin.template: 19 | src: templates/nano_rpc_cache_service.j2 20 | dest: "/etc/systemd/system/{{ nano_rpc_cache_daemon_name }}.service" 21 | mode: "644" 22 | become: true 23 | 24 | - name: "Stop service (if running)" 25 | ansible.builtin.systemd: 26 | name: "{{ nano_rpc_cache_daemon_name }}" 27 | state: stopped 28 | become: true 29 | 30 | - name: "Remove previous binary" 31 | ansible.builtin.file: 32 | dest: "{{ bin_dir }}/{{ nano_rpc_cache_bin }}" 33 | state: absent 34 | become: true 35 | 36 | - name: "Git checkout Nano RPC Cache repo" 37 | ansible.builtin.git: 38 | repo: "{{ nano_rpc_cache_git_repo }}" 39 | dest: /tmp/nano-rpc-cache 40 | force: true 41 | 42 | - name: "Make Nano RPC Cache binary" # noqa no-changed-when 43 | ansible.builtin.shell: 44 | cmd: "{{ nano_rpc_cache_make_command }}" 45 | chdir: /tmp/nano-rpc-cache 46 | environment: 47 | PATH: "{{ golang_bin_dir }}:{{ ansible_env.PATH }}" 48 | GOBIN: "{{ bin_dir }}" 49 | notify: restart_nano_rpc_cache_service 50 | become: true 51 | -------------------------------------------------------------------------------- /roles/nano/tasks/nano_work_server.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Get Nano work server binary version" # noqa no-changed-when 3 | ansible.builtin.command: 4 | cmd: "{{ nano_work_server_bin }} -V" 5 | chdir: "{{ bin_dir }}" 6 | failed_when: false 7 | register: nano_work_server_version_result 8 | 9 | - name: "Create systemd service file" 10 | ansible.builtin.template: 11 | src: templates/nano_work_server_service.j2 12 | dest: "/etc/systemd/system/{{ nano_work_server_daemon_name }}.service" 13 | mode: "644" 14 | notify: restart_nano_work_server_service 15 | become: true 16 | 17 | - name: "Build Nano work server binary" 18 | when: nano_work_server_version_result.stdout is not search(nano_work_server_version) 19 | block: 20 | - name: "Install Rust" # noqa command-instead-of-module 21 | ansible.builtin.shell: "curl https://sh.rustup.rs -sSf | sh -s -- -y" 22 | changed_when: false 23 | become: true 24 | 25 | - name: "Install apt packages" 26 | ansible.builtin.apt: 27 | pkg: 28 | - ocl-icd-opencl-dev 29 | - gcc 30 | become: true 31 | 32 | - name: "Git checkout" 33 | ansible.builtin.git: 34 | repo: "{{ nano_work_server_git_repo }}" 35 | dest: /tmp/nano-work-server 36 | version: "{{ nano_work_server_version }}" 37 | force: true 38 | 39 | - name: "Ensure Rust toolchain version" 40 | ansible.builtin.shell: 41 | cmd: "source ~/.cargo/env && rustup default {{ rust_version }}" 42 | executable: /bin/bash 43 | changed_when: false 44 | become: true 45 | 46 | - name: "Compile Nano work server binary" # noqa no-changed-when 47 | ansible.builtin.shell: 48 | cmd: source ~/.cargo/env && cargo build --release 49 | chdir: /tmp/nano-work-server 50 | executable: /bin/bash 51 | changed_when: false 52 | when: opencl_lib_path is not defined 53 | become: true 54 | 55 | - name: "Compile Nano work server binary (link OpenCL lib)" # noqa no-changed-when 56 | ansible.builtin.shell: 57 | cmd: "source ~/.cargo/env && cargo rustc --release -- -l OpenCL -L {{ opencl_lib_path }}" 58 | chdir: /tmp/nano-work-server 59 | executable: /bin/bash 60 | changed_when: false 61 | when: opencl_lib_path is defined 62 | become: true 63 | 64 | - name: "Copy compiled binary to bin folder" 65 | ansible.builtin.copy: 66 | src: /tmp/nano-work-server/target/release/nano-work-server 67 | dest: "{{ bin_dir }}/{{ nano_work_server_bin }}" 68 | owner: "{{ username }}" 69 | mode: "775" 70 | remote_src: true 71 | notify: restart_nano_work_server_service 72 | become: true 73 | -------------------------------------------------------------------------------- /roles/nano/templates/docker-compose.yml.j2: -------------------------------------------------------------------------------- 1 | 2 | version: "3.8" 3 | 4 | services: 5 | 6 | nano-node: 7 | image: nanocurrency/nano:{{ nano_version }} 8 | container_name: nano-node 9 | volumes: 10 | - nano-storage:/root 11 | ports: 12 | - 7075:7075 # Nano P2P 13 | - 7076:7076 # Nano RPC 14 | - 7078:7078 # Nano WebSockets 15 | restart: unless-stopped 16 | 17 | volumes: 18 | nano-storage: 19 | external: true 20 | -------------------------------------------------------------------------------- /roles/nano/templates/nano-node/config-node.toml.j2: -------------------------------------------------------------------------------- 1 | [node.websocket] 2 | 3 | # WebSocket server bind address 4 | # type:string,ip 5 | address = "::ffff:0.0.0.0" 6 | 7 | [rpc] 8 | 9 | # Enable or disable RPC 10 | # type:bool 11 | enable = true 12 | 13 | [node] 14 | work_peers = [ 15 | {% for peer in nano_work_server_peers %} 16 | "{{ peer }}"{{ ", " if not loop.last else "" }} 17 | {% endfor %} 18 | ] 19 | work_threads = 0 20 | use_peers = true -------------------------------------------------------------------------------- /roles/nano/templates/nano-node/config-rpc.toml.j2: -------------------------------------------------------------------------------- 1 | # Bind address for the RPC server 2 | # type:string,ip 3 | address = "::ffff:0.0.0.0" 4 | 5 | # Enable or disable control-level requests 6 | # type:bool 7 | enable_control = {{ nano_node_rpc_enable_control | string | lower }} 8 | -------------------------------------------------------------------------------- /roles/nano/templates/nano-rpc-cache/config.toml.j2: -------------------------------------------------------------------------------- 1 | [Main] 2 | # URL of the Nano RPC node. 3 | NodeRpc = "{{ nano_node_rpc_url }}" 4 | 5 | # URL of the Nano Worker Server. 6 | # Can be the same as NodeRpc, or different. Empty also means the same. 7 | # NOTE: Currently supports only single Work Server. 8 | NodeRpcWork = "{{ nano_rpc_cache_work_server_url }}" 9 | 10 | # Binding address of the service, ":7176" by default 11 | ListenIpPort = ":{{ nano_rpc_cache_port }}" 12 | 13 | # Cachefile name: if set, this file is used to persist the cache content. 14 | # Should be full or relative path. If it is empty, persistence to file is not used. 15 | {% if nano_rpc_cache_filename | length > 0 %} 16 | CachePeristFileName = "{{ nano_rpc_cache_filename }}" 17 | {% else %} 18 | # CachePeristFileName = ".cache" 19 | {% endif %} 20 | 21 | # HTTP API Rate limit: max concurrent outstanding requests. If reached, overload error messages are returned. 22 | # Background-running work requests are counted in here, they are limited differently 23 | RestMaxActiveRequests = {{ nano_rpc_cache_max_active_requests }} 24 | 25 | # BackgroundWorkerCount: number of background worker threads, and also maximum number of outgoing requests from pregenerations 26 | # Range: 2 - 20, default 4 27 | BackgroundWorkerCount = {{ nano_rpc_cache_worker_count }} 28 | 29 | # MaxOutRequests: number of concurrent outgoing work request (boisth from generation and pregeneration). 30 | # Range: 3 - 30 or 0, default 8, but must be at least 1 larger than BackgroundWorkerCount 31 | MaxOutRequests = {{ nano_rpc_cache_max_active_work_generation_requests }} 32 | 33 | # EnablePregeneration: enable pregeneration -- computing of work in advance when e.g. balance is retrieved 34 | # Range: 0 or 1, default 0 35 | EnablePregeneration = {{ 1 if nano_rpc_cache_pregeneration_enabled else 0 }} 36 | 37 | # PregenerationQueueSize: maximum size of queue for pregenerate requests 38 | # Range: 0 - 100000, default 1000 39 | PregenerationQueueSize = {{ nano_rpc_cache_pregeneration_queue_size }} 40 | 41 | # MaxCacheAgeDays: age limit on old cache entries for cache aging. 0 means no cache aging. 42 | # Dafault: 30 (days) 43 | MaxCacheAgeDays = {{ nano_rpc_cache_age_limit_in_days }} 44 | -------------------------------------------------------------------------------- /roles/nano/templates/nano_rpc_cache_service.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | [Unit] 4 | Description=Nano RPC Cache Daemon 5 | After=network.target 6 | 7 | [Service] 8 | Type=simple 9 | User={{ username }} 10 | ExecStart={{ bin_dir }}/{{ nano_rpc_cache_bin }} {{ nano_rpc_cache_config_path }} 11 | Restart=on-failure 12 | RestartSec=3 13 | LimitNOFILE=4096 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /roles/nano/templates/nano_work_server_service.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | [Unit] 4 | Description=Nano Work Server Daemon 5 | After=network.target 6 | 7 | [Service] 8 | Type=simple 9 | User={{ username }} 10 | ExecStart={{ bin_dir }}/{{ nano_work_server_bin }} --gpu 0:0 --listen-address {{ nano_work_server_listen_address }} 11 | Restart=on-failure 12 | RestartSec=3 13 | LimitNOFILE=4096 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /roles/near/README.md: -------------------------------------------------------------------------------- 1 | ../../docs/near_role.md -------------------------------------------------------------------------------- /roles/near/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # is the non-root user under which daemon will operate 3 | username: near 4 | 5 | # is a user home dir 6 | home_dir: "/home/{{ username }}" 7 | 8 | # is the near binary version. When doesn't match, will rebuild binary from sources. 9 | near_version: 1.29.0 10 | 11 | # is the chain executable 12 | near_bin: /usr/local/bin/neard 13 | 14 | # is the near data dir 15 | data_dir: "{{ home_dir }}/.near" 16 | 17 | # is the near/nearcore GitHub project checkout dir, default is /tmp/nearcore 18 | checkout_dir: /tmp/nearcore 19 | 20 | # is the near chain id 21 | near_chain_id: mainnet 22 | 23 | # is the near remote config 24 | near_config_json_url: "https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/nearcore-deploy/{{ near_chain_id }}/config.json" 25 | 26 | # defines SystemD service name 27 | near_daemon_name: neard 28 | 29 | # defines the chain data file to download. 30 | # Avaialable values are rpc, archive and none (sync from beginning). 31 | quicksync_mode: rpc 32 | 33 | # forces the chain data download step. 34 | # Warning, the content of the data dir will be wiped. 35 | # By default chain data will be downloaded only after chain init. 36 | quicksync_force: false 37 | -------------------------------------------------------------------------------- /roles/near/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Restart neard service" 3 | ansible.builtin.systemd: 4 | name: "{{ near_daemon_name }}" 5 | state: restarted 6 | daemon_reload: true 7 | enabled: true 8 | listen: restart_service 9 | -------------------------------------------------------------------------------- /roles/near/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: vorotech 4 | description: Ansible role to manage NEAR blockchain node 5 | company: Trust Wallet 6 | license: MIT 7 | min_ansible_version: 2.10.6 8 | platforms: 9 | - name: Ubuntu 10 | versions: 11 | - all 12 | galaxy_tags: 13 | - blockchain 14 | - near 15 | dependencies: [] 16 | -------------------------------------------------------------------------------- /roles/near/tasks/init.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://stackoverflow.com/a/56379678/166473 3 | - name: "Install acl package" 4 | ansible.builtin.apt: 5 | update_cache: true 6 | cache_valid_time: 86400 7 | pkg: acl 8 | 9 | - name: "Init NEAR {{ near_chain_id }} chain" 10 | ansible.builtin.command: 11 | cmd: > 12 | {{ near_bin }} --home {{ data_dir }} init --chain-id={{ near_chain_id }} --download-genesis --download-config 13 | creates: "{{ data_dir }}/node_key.json" 14 | become: true 15 | become_user: "{{ username }}" 16 | register: init_result 17 | 18 | - name: "Download the config file (instead default)" # noqa no-handler 19 | ansible.builtin.get_url: 20 | url: "{{ near_config_json_url }}" 21 | dest: "{{ data_dir }}/config.json" 22 | force: true 23 | mode: "640" 24 | become: true 25 | become_user: "{{ username }}" 26 | when: init_result.changed 27 | notify: restart_service 28 | -------------------------------------------------------------------------------- /roles/near/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Create user {{ username }}" 3 | ansible.builtin.user: 4 | name: "{{ username }}" 5 | 6 | - name: "Create systemd service file" 7 | ansible.builtin.template: 8 | src: templates/service.j2 9 | dest: "/etc/systemd/system/{{ near_daemon_name }}.service" 10 | mode: "644" 11 | notify: restart_service 12 | 13 | - name: "Get NEAR binary version" # noqa no-changed-when 14 | ansible.builtin.command: 15 | cmd: "{{ near_bin }} -V" 16 | failed_when: false 17 | register: near_version_result 18 | 19 | - name: "Build NEAR binary" 20 | when: near_version_result.stdout is not search(near_version) 21 | block: 22 | - name: "Install Rust" # noqa command-instead-of-module 23 | ansible.builtin.shell: "curl https://sh.rustup.rs -sSf | sh -s -- -y" 24 | changed_when: false 25 | 26 | - name: "Install apt packages" 27 | ansible.builtin.apt: 28 | update_cache: true 29 | cache_valid_time: 86400 30 | pkg: 31 | - binutils-dev 32 | - clang 33 | - cmake 34 | - g++ 35 | - gcc 36 | - libcurl4-openssl-dev 37 | - libdw-dev 38 | - libiberty-dev 39 | - libssl-dev 40 | - llvm 41 | - pkg-config 42 | - protobuf-compiler 43 | - zlib1g-dev 44 | 45 | - name: "Git checkout" 46 | ansible.builtin.git: 47 | repo: https://github.com/near/nearcore 48 | dest: "{{ checkout_dir }}" 49 | version: "{{ near_version }}" 50 | force: true 51 | 52 | - name: "Compile nearcore binary" # noqa no-changed-when 53 | ansible.builtin.shell: 54 | cmd: source ~/.cargo/env && make release 55 | chdir: "{{ checkout_dir }}" 56 | executable: /bin/bash 57 | changed_when: false 58 | async: 72000 59 | poll: 10 60 | 61 | - name: "Copy compiled binary to bin folder" 62 | ansible.builtin.copy: 63 | src: "{{ checkout_dir }}/target/release/neard" 64 | dest: "{{ near_bin }}" 65 | owner: "{{ username }}" 66 | mode: "775" 67 | remote_src: true 68 | notify: restart_service 69 | 70 | - name: "Ensure creation of data dir" 71 | ansible.builtin.file: 72 | dest: "{{ data_dir }}" 73 | state: directory 74 | owner: "{{ username }}" 75 | mode: "774" 76 | -------------------------------------------------------------------------------- /roles/near/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: install.yml 3 | - include_tasks: init.yml 4 | - include_tasks: sync.yml 5 | when: 6 | - init_result.changed or quicksync_force 7 | - quicksync_mode != 'none' 8 | -------------------------------------------------------------------------------- /roles/near/tasks/sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Install apt packages" 3 | ansible.builtin.apt: 4 | update_cache: true 5 | cache_valid_time: 86400 6 | pkg: python3-pip 7 | 8 | - name: "Install awscli pip package" 9 | ansible.builtin.pip: 10 | name: awscli 11 | 12 | - name: "Get latest target" 13 | ansible.builtin.command: 14 | cmd: > 15 | aws s3 cp --no-sign-request --no-progress 16 | s3://near-protocol-public/backups/mainnet/{{ quicksync_mode }}/latest . 17 | chdir: /tmp 18 | changed_when: false 19 | 20 | - name: "Stop service (if running)" 21 | ansible.builtin.systemd: 22 | name: "{{ near_daemon_name }}" 23 | state: stopped 24 | 25 | - name: "Delete inner data dir" 26 | ansible.builtin.file: 27 | path: "{{ data_dir }}/data" 28 | state: absent 29 | 30 | - name: "Download latest data" 31 | ansible.builtin.shell: 32 | cmd: > 33 | aws s3 cp --no-sign-request --recursive --no-progress 34 | s3://near-protocol-public/backups/mainnet/{{ quicksync_mode }}/$(cat /tmp/latest) 35 | {{ data_dir }}/data 36 | async: 72000 37 | poll: 10 38 | become: true 39 | become_user: "{{ username }}" 40 | changed_when: false 41 | notify: restart_service 42 | -------------------------------------------------------------------------------- /roles/near/templates/service.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | [Unit] 4 | Description=NEAR Daemon 5 | After=network.target 6 | 7 | [Service] 8 | Type=simple 9 | User={{ username }} 10 | ExecStart={{ near_bin }} --home={{ data_dir }} run 11 | Restart=on-failure 12 | RestartSec=3 13 | LimitNOFILE=65535 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /roles/polygon/README.md: -------------------------------------------------------------------------------- 1 | # Polygon Role 2 | 3 | Ansible role to manage Polygon blockchain node. 4 | 5 | The role is based on the original [Polygon Ansible playbook](https://github.com/maticnetwork/node-ansible), 6 | but contains automatic snapshot sync. 7 | 8 | NOTE: The role currently sets up only the Full RPC node. 9 | To set up the `sentry/validator` node feel free 10 | to follow the original instruction or submit a Pull Request. 11 | 12 | ## Requirements 13 | 14 | ### Golang 15 | 16 | This role doesn't attempt to install the golang, but assumes it is available 17 | at the default location `/usr/local/go/bin`. The default location can be overridden 18 | with the `golang_bin_dir` variable when executing the role. 19 | 20 | Tip: Golang can be installed with Ansible Galaxy [gantsign.golang](https://galaxy.ansible.com/gantsign/golang) role or similar. 21 | 22 | 23 | ## Role Variables 24 | 25 | The role has default variables (see `defaults/main.yml`) which can be adjusted. 26 | 27 | ## Example Playbook 28 | 29 | ```yaml 30 | - hosts: "all" 31 | gather_facts: true 32 | become: true 33 | 34 | roles: 35 | - role: gantsign.golang 36 | golang_version: "1.18.1" 37 | golang_install_dir: /usr/local/go 38 | 39 | - role: trustwallet.blockchain.polygon 40 | data_dir: /mnt/data # exampe of non-default data directory, default is /home/polygon 41 | quicksync_mode: full # options are archive, full, pruned and none (sync bor from scratch) 42 | bor_config: 43 | HTTPModules: 44 | - eth 45 | - net 46 | - web3 47 | - txpool 48 | - bor 49 | ``` 50 | 51 | ## Popular Questions 52 | 53 | ### What ports does Polygon blockchain node use? 54 | 55 | The sentry machine must have the following ports open: 56 | 57 | * `26656` - Heimdall P2P port to connect the node to other nodes, should be open to the world `0.0.0.0/0`. 58 | 59 | * `26657` - Heimdall RPC port, should not be exposed to the world. 60 | 61 | * `1317` - Heimdall REST API port, will be used by Bor. 62 | 63 | * `30303` - Bor P2P port to connect the node to other nodes,should be open to the world `0.0.0.0/0`. 64 | 65 | * `8545` - Bor RPC port (used for HTTP API and websocket). 66 | 67 | * `22` - For the validator to be able to ssh open to the specific subnet where Validator is configured. 68 | 69 | 70 | ### What are the sizes of Polygon Chains Snapshots? 71 | 72 | * Mainnet Archive Bor snapshot ~10TiB 73 | * Mainnet FullNode Bor snapshot ~870GiB 74 | * Mainnet Pruned Bor snapshot ~700GiB 75 | * Mainnet Heimdall snapshot ~130GiB 76 | 77 | ## Development 78 | 79 | To aid in the development and testing of the Ansible role, we are 80 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 81 | 82 | ```shell 83 | molecule -v test -s polygon 84 | ``` 85 | 86 | ## References 87 | 88 | * [Trust Wallet](https://trustwallet.com) 89 | * [Original Polygon Full Node Deployment with Ansible](https://docs.polygon.technology/docs/develop/network-details/full-node-deployment/) 90 | * [Polygon Chains Snapshots](https://snapshots.matic.today/) 91 | * [Snapshot Instructions for Heimdall and Bor](https://forum.matic.network/t/snapshot-instructions-for-heimdall-and-bor/2278) 92 | * Alternatively, [Run Polygon node with Docker](https://chasewright.com/how-to-run-a-polygon-matic-mainnet-node/) 93 | 94 | ## License 95 | 96 | MIT 97 | -------------------------------------------------------------------------------- /roles/polygon/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # is the non-root user under which daemon will operate 3 | username: polygon 4 | 5 | # is a user home dir 6 | home_dir: "/home/{{ username }}" 7 | 8 | data_dir: "{{ home_dir }}" 9 | 10 | # is a binaries location dir, should be in the PATH. 11 | bin_dir: /usr/local/bin 12 | 13 | # specifies Bor syncmode and snapshot type. 14 | # Options are archive, full, pruned and none (sync bor from scratch) 15 | quicksync_mode: pruned 16 | 17 | # is the maximum number of connections to the server for each download. 18 | quicksync_connection_num: 5 19 | 20 | # forces the chain data download step. 21 | # Warning, the content of the data dir will be wiped. 22 | # By default chain data will be downloaded only after chain init. 23 | quicksync_force: false 24 | 25 | # forces only Heimdall data download step. 26 | # Warning, the content of the data dir will be wiped. 27 | # By default chain data will be downloaded only after chain init. 28 | quicksync_force_heimdall: false 29 | 30 | # forces only Bor data download step. 31 | # Warning, the content of the data dir will be wiped. 32 | # By default chain data will be downloaded only after chain init. 33 | quicksync_force_bor: false 34 | 35 | # skips steps to download quicksync chain data archive, 36 | # assuming that the file has been already downloaded. 37 | quicksync_skip_download: false 38 | 39 | # is the temporary dir to keep downloaded file. 40 | # The aria2c package is utilized to speed up download process, 41 | # but it requires double size to first download and then unpack data archive. 42 | # In cloud environment the download dir often points to temporary mounted drive. 43 | quicksync_tmp_dir: /tmp/quicksync 44 | 45 | heimdall_dir: "{{ data_dir }}/.heimdalld" 46 | 47 | heimdall_client_dir: "{{ data_dir }}/.heimdallcli" 48 | 49 | # is the heimdall release version. 50 | # See https://github.com/maticnetwork/heimdall/releases/ 51 | heimdall_version: v0.2.10 52 | 53 | # is the custom username of your node 54 | heimdall_moniker: moniker-1 55 | 56 | heimdall_seeds: "f4f605d60b8ffaaf15240564e58a81103510631c@159.203.9.164:26656,4fb1bc820088764a564d4f66bba1963d47d82329@44.232.55.71:26656,2eadba4be3ce47ac8db0a3538cb923b57b41c927@35.199.4.13:26656,3b23b20017a6f348d329c102ddc0088f0a10a444@35.221.13.28:26656,25f5f65a09c56e9f1d2d90618aa70cd358aa68da@35.230.116.151:26656" 57 | 58 | heimdall_daemon_name: heimdalld 59 | 60 | heimdall_extra_run_args: "" 61 | 62 | heimdall_rest_server_daemon_name: heimdalld-rest-server 63 | 64 | heimdall_rest_server_extra_run_args: "" 65 | 66 | # is the Heimdall service network, could be mainnet or mumbai (testnet) 67 | heimdall_network: mainnet 68 | 69 | heimdall_genesis_json_url: https://raw.githubusercontent.com/maticnetwork/launch/master/mainnet-v1/sentry/sentry/heimdall/config/genesis.json 70 | 71 | heimdall_max_open_connections: 100 72 | 73 | bor_dir: "{{ data_dir }}/.bor" 74 | 75 | # is the bor release version. 76 | # See https://github.com/maticnetwork/bor/releases/ 77 | bor_version: v0.2.16 78 | 79 | bor_bootnodes: "enode://0cb82b395094ee4a2915e9714894627de9ed8498fb881cec6db7c65e8b9a5bd7f2f25cc84e71e89d0947e51c76e85d0847de848c7782b13c0255247a6758178c@44.232.55.71:30303,enode://88116f4295f5a31538ae409e4d44ad40d22e44ee9342869e7d68bdec55b0f83c1530355ce8b41fbec0928a7d75a5745d528450d30aec92066ab6ba1ee351d710@159.203.9.164:30303,enode://3178257cd1e1ab8f95eeb7cc45e28b6047a0432b2f9412cff1db9bb31426eac30edeb81fedc30b7cd3059f0902b5350f75d1b376d2c632e1b375af0553813e6f@35.221.13.28:30303,enode://16d9a28eadbd247a09ff53b7b1f22231f6deaf10b86d4b23924023aea49bfdd51465b36d79d29be46a5497a96151a1a1ea448f8a8666266284e004306b2afb6e@35.199.4.13:30303,enode://ef271e1c28382daa6ac2d1006dd1924356cfd843dbe88a7397d53396e0741ca1a8da0a113913dee52d9071f0ad8d39e3ce87aa81ebc190776432ee7ddc9d9470@35.230.116.151:30303" 80 | 81 | bor_daemon_name: bor 82 | 83 | bor_genesis_json_url: https://raw.githubusercontent.com/maticnetwork/launch/master/mainnet-v1/sentry/sentry/bor/genesis.json 84 | 85 | # is an optinal extra flags 86 | bor_extra_run_args: "--ipcdisable" 87 | 88 | # is the location of the golang bin folder which will be added 89 | # to the $PATH. 90 | golang_bin_dir: /usr/local/go/bin 91 | 92 | eth_rpc_url: "" 93 | 94 | # is reserved for future to setup Sentry nodes. 95 | validator_address: "" 96 | 97 | network_id: 137 98 | 99 | bor_config: {} 100 | 101 | default_bor_config: 102 | Eth: 103 | SyncMode: "full" 104 | NetworkId: "{{ network_id }}" 105 | Node: 106 | DataDir: "{{ bor_dir }}/data" 107 | KeyStoreDir: "{{ bor_dir }}/keystore" 108 | HTTPHost: "0.0.0.0" 109 | HTTPPort: 8545 110 | HTTPCors: 111 | - "*" 112 | HTTPVirtualHosts: 113 | - "*" 114 | HTTPModules: 115 | - eth 116 | - net 117 | - web3 118 | - bor 119 | Node.P2P: 120 | MaxPeers: 200 121 | Metrics: 122 | Enabled: false 123 | HTTP: "0.0.0.0" 124 | Port: 6060 125 | -------------------------------------------------------------------------------- /roles/polygon/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Restart heimdalld service" 3 | ansible.builtin.systemd: 4 | name: "{{ heimdall_daemon_name }}" 5 | state: restarted 6 | daemon_reload: true 7 | enabled: true 8 | listen: restart_heimdall_service 9 | 10 | - name: "Restart heimdall_rest_server service" 11 | ansible.builtin.systemd: 12 | name: "{{ heimdall_rest_server_daemon_name }}" 13 | state: restarted 14 | daemon_reload: true 15 | enabled: true 16 | listen: restart_heimdall_service 17 | 18 | - name: "Restart bor service" 19 | ansible.builtin.systemd: 20 | name: "{{ bor_daemon_name }}" 21 | state: restarted 22 | daemon_reload: true 23 | enabled: true 24 | listen: restart_bor_service 25 | -------------------------------------------------------------------------------- /roles/polygon/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: vorotech 4 | description: Ansible role to manage Polygon blockchain node 5 | company: Trust Wallet 6 | license: MIT 7 | min_ansible_version: 2.10.6 8 | platforms: 9 | - name: Ubuntu 10 | versions: 11 | - all 12 | galaxy_tags: 13 | - blockchain 14 | - polygon 15 | dependencies: [] 16 | -------------------------------------------------------------------------------- /roles/polygon/tasks/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "heimdall - Update moniker in config.toml" # noqa risky-file-permissions 3 | community.general.ini_file: 4 | path: "{{ heimdall_dir }}/config/config.toml" 5 | section: "" 6 | option: "moniker" 7 | value: "\"{{ heimdall_moniker }}\"" 8 | notify: restart_heimdall_service 9 | 10 | - name: "heimdall - Update seeds in config.toml" # noqa risky-file-permissions 11 | community.general.ini_file: 12 | path: "{{ heimdall_dir }}/config/config.toml" 13 | section: "p2p" 14 | option: "seeds" 15 | value: "\"{{ heimdall_seeds }}\"" 16 | notify: restart_heimdall_service 17 | 18 | - name: "heimdall - Update cors_allowed_origins in config.toml" # noqa risky-file-permissions 19 | community.general.ini_file: 20 | path: "{{ heimdall_dir }}/config/config.toml" 21 | section: "rpc" 22 | option: "cors_allowed_origins" 23 | value: "[\"*\"]" 24 | notify: restart_heimdall_service 25 | 26 | - name: "heimdall - Assert eth_rpc_url is defined" 27 | ansible.builtin.assert: 28 | that: 29 | - eth_rpc_url | default('') | length > 0 30 | fail_msg: "missed required 'eth_rpc_url' – an RPC endpoint for a fully synced Ethereum mainnet node, i.e Infura" 31 | 32 | - name: "heimdall - Update eth_rpc_url in heimdall-config.toml" # noqa risky-file-permissions 33 | community.general.ini_file: 34 | path: "{{ heimdall_dir }}/config/heimdall-config.toml" 35 | section: "" 36 | option: "eth_rpc_url" 37 | value: "\"{{ eth_rpc_url }}\"" 38 | notify: restart_heimdall_service 39 | 40 | - name: "bor - Merge default and custom config" 41 | ansible.builtin.set_fact: 42 | final_bor_config: "{{ default_bor_config | combine(bor_config, recursive=true) }}" 43 | 44 | - name: "bor - Copy final config in place" 45 | ansible.builtin.template: 46 | src: bor-config.toml.j2 47 | dest: "{{ bor_dir }}/config.toml" 48 | owner: "{{ username }}" 49 | group: "{{ username }}" 50 | mode: "644" 51 | when: 52 | - final_bor_config is defined 53 | - final_bor_config.keys() | length > 0 54 | notify: restart_bor_service 55 | -------------------------------------------------------------------------------- /roles/polygon/tasks/init.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://stackoverflow.com/a/56379678/166473 3 | - name: "Install acl package" 4 | ansible.builtin.apt: 5 | update_cache: true 6 | cache_valid_time: 86400 7 | pkg: acl 8 | 9 | - name: "Ensure data dir permissions" 10 | ansible.builtin.file: 11 | dest: "{{ data_dir }}" 12 | state: directory 13 | owner: "{{ username }}" 14 | mode: "774" 15 | 16 | - name: "heimdall - Init" 17 | ansible.builtin.command: 18 | cmd: > 19 | {{ bin_dir }}/heimdalld --home {{ heimdall_dir }} --home-client {{ heimdall_client_dir }} init 20 | creates: "{{ heimdall_dir }}/config/config.toml" 21 | become: true 22 | become_user: "{{ username }}" 23 | register: heimdall_init_result 24 | 25 | - name: "heimdall - Download the genesis file (instead default)" # noqa no-handler 26 | ansible.builtin.get_url: 27 | url: "{{ heimdall_genesis_json_url }}" 28 | dest: "{{ heimdall_dir }}/config/genesis.json" 29 | force: true 30 | mode: "640" 31 | become: true 32 | become_user: "{{ username }}" 33 | when: heimdall_init_result.changed 34 | notify: restart_heimdall_service 35 | 36 | - name: "bor - Override bor_data_dir from specified config" 37 | ansible.builtin.set_fact: 38 | bor_data_dir: "{{ bor_config.Node.DataDir }}" 39 | when: 40 | - bor_config is defined 41 | - bor_config.Node is defined 42 | - bor_config.Node.DataDir is defined 43 | 44 | - name: "bor - Set default bor_data_dir" 45 | ansible.builtin.set_fact: 46 | bor_data_dir: "{{ bor_dir }}/data" 47 | when: 48 | - bor_data_dir is not defined 49 | 50 | - name: "bor - Ensure bor dirs exist" 51 | ansible.builtin.file: 52 | dest: "{{ item }}" 53 | state: directory 54 | owner: "{{ username }}" 55 | mode: "774" 56 | loop: 57 | - "{{ bor_dir }}" 58 | - "{{ bor_data_dir }}/bor" 59 | 60 | - name: "bor - Download the genesis file" # noqa no-handler 61 | ansible.builtin.get_url: 62 | url: "{{ bor_genesis_json_url }}" 63 | dest: "{{ bor_dir }}/genesis.json" 64 | mode: "640" 65 | become: true 66 | become_user: "{{ username }}" 67 | 68 | - name: "bor - Create nodekey" 69 | ansible.builtin.command: 70 | cmd: "{{ bin_dir }}/bootnode -genkey {{ bor_data_dir }}/bor/nodekey" 71 | creates: "{{ bor_data_dir }}/bor/nodekey" 72 | become: true 73 | become_user: "{{ username }}" 74 | 75 | - name: "bor - Init" 76 | ansible.builtin.command: 77 | cmd: "{{ bin_dir }}/bor init --datadir {{ bor_data_dir }} {{ bor_dir }}/genesis.json" 78 | creates: "{{ bor_data_dir }}/keystore" 79 | become: true 80 | become_user: "{{ username }}" 81 | register: bor_init_result 82 | -------------------------------------------------------------------------------- /roles/polygon/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Create user {{ username }}" 3 | ansible.builtin.user: 4 | name: "{{ username }}" 5 | 6 | - name: "Create systemd service files" 7 | ansible.builtin.template: 8 | src: templates/{{ item }}.service.j2 9 | dest: "/etc/systemd/system/{{ item }}.service" 10 | mode: "644" 11 | loop: 12 | - "{{ heimdall_daemon_name }}" 13 | - "{{ heimdall_rest_server_daemon_name }}" 14 | - "{{ bor_daemon_name }}" 15 | notify: 16 | - restart_heimdall_service 17 | - restart_bor_service 18 | 19 | - name: "Install apt packages" 20 | ansible.builtin.apt: 21 | update_cache: true 22 | cache_valid_time: 86400 23 | pkg: 24 | - build-essential 25 | - git 26 | - jq 27 | - python3-pip 28 | - rabbitmq-server 29 | 30 | - name: "heimdall - Get binary version" 31 | ansible.builtin.command: 32 | cmd: "{{ bin_dir }}/heimdalld version" 33 | changed_when: false 34 | failed_when: false 35 | register: heimdall_version_result 36 | 37 | - name: "heimdall - Build binary" 38 | when: heimdall_version_result.stdout is not search(heimdall_version | replace('v','')) 39 | block: 40 | 41 | - name: "heimdall - Git checkout" 42 | ansible.builtin.git: 43 | repo: https://github.com/maticnetwork/heimdall.git 44 | dest: "{{ home_dir }}/heimdall" 45 | version: "{{ heimdall_version }}" 46 | force: true 47 | 48 | - name: "heimdall - Build binary" 49 | ansible.builtin.command: 50 | cmd: "make install network={{ heimdall_network }}" 51 | chdir: "{{ home_dir }}/heimdall" 52 | environment: 53 | PATH: "{{ golang_bin_dir }}:{{ ansible_env.PATH }}" 54 | GOBIN: "{{ bin_dir }}" 55 | notify: restart_heimdall_service 56 | 57 | - name: "heimdall - Get binary version" 58 | ansible.builtin.command: 59 | cmd: "{{ bin_dir }}/heimdalld version" 60 | 61 | - name: "bor - Get binary version" 62 | ansible.builtin.command: 63 | cmd: "{{ bin_dir }}/bor version" 64 | changed_when: false 65 | failed_when: false 66 | register: bor_version_result 67 | 68 | - name: "heimdall - Build binary" 69 | when: bor_version_result.stdout is not search(bor_version | replace('v','')) 70 | block: 71 | 72 | - name: "bor - Git checkout" 73 | ansible.builtin.git: 74 | repo: https://github.com/maticnetwork/bor.git 75 | dest: "{{ home_dir }}/bor" 76 | version: "{{ bor_version }}" 77 | force: true 78 | 79 | - name: "bor - Build binary" 80 | ansible.builtin.command: 81 | cmd: "make bor-all" 82 | chdir: "{{ home_dir }}/bor" 83 | environment: 84 | PATH: "{{ golang_bin_dir }}:{{ ansible_env.PATH }}" 85 | GOBIN: "{{ bin_dir }}" 86 | 87 | - name: "bor - Creating symlink for bor" 88 | file: 89 | src: "{{ home_dir }}/bor/build/bin/bor" 90 | dest: "{{ bin_dir }}/bor" 91 | state: link 92 | notify: restart_bor_service 93 | 94 | - name: "bor - Creating symlink for bootnode" 95 | file: 96 | src: "{{ home_dir }}/bor/build/bin/bootnode" 97 | dest: "{{ bin_dir }}/bootnode" 98 | state: link 99 | 100 | - name: "bor - Get binary version" 101 | ansible.builtin.command: 102 | cmd: "{{ bin_dir }}/bor version" 103 | -------------------------------------------------------------------------------- /roles/polygon/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: install.yml 3 | 4 | - include_tasks: init.yml 5 | 6 | - include_tasks: config.yml 7 | 8 | - include_tasks: sync.yml 9 | when: 10 | - heimdall_init_result.changed or bor_init_result.changed or quicksync_force or quicksync_force_heimdall or quicksync_force_bor 11 | - quicksync_mode != 'none' 12 | -------------------------------------------------------------------------------- /roles/polygon/tasks/sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Install apt packages" 3 | ansible.builtin.apt: 4 | update_cache: true 5 | cache_valid_time: 86400 6 | pkg: 7 | - libxml2-utils # xmllint 8 | - aria2 9 | 10 | - name: "Ensure download dir permissions" 11 | ansible.builtin.file: 12 | dest: "{{ quicksync_tmp_dir }}" 13 | state: directory 14 | mode: "777" 15 | 16 | - name: "Set Network title" 17 | ansible.builtin.set_fact: 18 | network_title: "{{ (heimdall_network == 'mainnet') | ternary('Mainnet', 'TestNet') }}" 19 | 20 | - name: "Set Archive Bor snapshot" 21 | ansible.builtin.set_fact: 22 | bor_snapshot_title: Archive 23 | when: quicksync_mode == 'archive' 24 | 25 | - name: "Set FullNode Bor snapshot" 26 | ansible.builtin.set_fact: 27 | bor_snapshot_title: FullNode 28 | when: quicksync_mode == 'full' 29 | 30 | - name: "Set Pruned Bor snapshot" 31 | ansible.builtin.set_fact: 32 | bor_snapshot_title: Pruned 33 | when: quicksync_mode == 'pruned' 34 | 35 | - name: "Compose titles" 36 | ansible.builtin.set_fact: 37 | bor_title: "{{ network_title }} {{ bor_snapshot_title }} Bor" 38 | heimdall_title: "{{ network_title }} Heimdall" 39 | 40 | - name: "heimdall - Download chain data" 41 | when: 42 | - heimdall_init_result.changed or quicksync_force or quicksync_force_heimdall 43 | - quicksync_mode != 'none' 44 | block: 45 | - name: "heimdall - Get snapshot URL" 46 | ansible.builtin.shell: 47 | cmd: > 48 | curl https://snapshots.matic.today/ | xmllint --html --xpath '//td[contains(text(), "{{ heimdall_title }}")]/following-sibling::td/a/text()' - 49 | changed_when: true 50 | register: heimdall_snapshot_url_result 51 | 52 | - name: "heimdall - Set snapshot URL and filename" 53 | ansible.builtin.set_fact: 54 | heimdall_snapshot_url: "{{ heimdall_snapshot_url_result.stdout }}" 55 | heimdall_snapshot_filename: "{{ heimdall_snapshot_url_result.stdout | basename }}" 56 | 57 | - name: "heimdall - Find downloaded data archive (if skip download)" 58 | ansible.builtin.find: 59 | paths: "{{ quicksync_tmp_dir }}" 60 | patterns: "heimdall-*.tar.gz" 61 | register: local_heimdall_data 62 | when: quicksync_skip_download 63 | 64 | - name: "heimdall - Override target filename (if skip download)" 65 | ansible.builtin.set_fact: 66 | heimdall_snapshot_filename: "{{ local_heimdall_data.files[0] }}" 67 | when: 68 | - quicksync_skip_download 69 | - local_heimdall_data.files | length > 0 70 | 71 | - name: "heimdall - Download snapshot archive" 72 | ansible.builtin.shell: 73 | cmd: "aria2c --continue --max-connection-per-server={{ quicksync_connection_num }} {{ heimdall_snapshot_url }} > heimdall_aria.log" 74 | chdir: "{{ quicksync_tmp_dir }}" 75 | async: 72000 76 | poll: 10 77 | when: not quicksync_skip_download 78 | 79 | - name: "heimdall - Stop services (if running)" 80 | ansible.builtin.systemd: 81 | name: "{{ item }}" 82 | state: stopped 83 | loop: 84 | - "{{ heimdall_daemon_name }}" 85 | - "{{ heimdall_rest_server_daemon_name }}" 86 | 87 | - name: "heimdall - Delete inner data dir" 88 | ansible.builtin.file: 89 | path: "{{ heimdall_dir }}/data" 90 | state: absent 91 | notify: restart_heimdall_service 92 | 93 | - name: "heimdall - Create inner chain data dir" 94 | ansible.builtin.file: 95 | path: "{{ heimdall_dir }}/data" 96 | state: directory 97 | owner: "{{ username }}" 98 | group: "{{ username }}" 99 | mode: "755" 100 | 101 | - name: "heimdall - Extract chain data" 102 | ansible.builtin.command: 103 | cmd: "tar -xf {{ quicksync_tmp_dir }}/{{ heimdall_snapshot_filename }}" 104 | chdir: "{{ heimdall_dir }}/data" 105 | async: 72000 106 | poll: 10 107 | become: true 108 | become_user: "{{ username }}" 109 | changed_when: false 110 | 111 | - name: "bor - Download chain data" 112 | when: 113 | - bor_init_result.changed or quicksync_force or quicksync_force_bor 114 | - quicksync_mode != 'none' 115 | block: 116 | 117 | - name: "bor - Get snapshot URL" 118 | ansible.builtin.shell: 119 | cmd: > 120 | curl https://snapshots.matic.today/ | xmllint --html --xpath '//td[contains(text(), "{{ bor_title }}")]/following-sibling::td/a/text()' - 121 | changed_when: true 122 | register: bor_snapshot_url_result 123 | 124 | - name: "bor - Set snapshot URL and filename" 125 | ansible.builtin.set_fact: 126 | bor_snapshot_url: "{{ bor_snapshot_url_result.stdout }}" 127 | bor_snapshot_filename: "{{ bor_snapshot_url_result.stdout | basename }}" 128 | 129 | - name: "bor - Find downloaded data archive (if skip download)" 130 | ansible.builtin.find: 131 | paths: "{{ quicksync_tmp_dir }}" 132 | patterns: "bor-*.tar.gz" 133 | register: local_bor_data 134 | when: quicksync_skip_download 135 | 136 | - name: "bor - Override target filename (if skip download)" 137 | ansible.builtin.set_fact: 138 | bor_snapshot_filename: "{{ local_bor_data.files[0] }}" 139 | when: 140 | - quicksync_skip_download 141 | - local_bor_data.files | length > 0 142 | 143 | - name: "bor - Download snapshot archive" 144 | ansible.builtin.shell: 145 | cmd: "aria2c --continue --max-connection-per-server={{ quicksync_connection_num }} {{ bor_snapshot_url }} > bor_aria.log" 146 | chdir: "{{ quicksync_tmp_dir }}" 147 | async: 72000 148 | poll: 10 149 | when: not quicksync_skip_download 150 | 151 | - name: "bor - Stop services (if running)" 152 | ansible.builtin.systemd: 153 | name: "{{ bor_daemon_name }}" 154 | state: stopped 155 | 156 | - name: "bor - Delete inner chain data dir" 157 | ansible.builtin.file: 158 | path: "{{ bor_data_dir }}/bor/chaindata" 159 | state: absent 160 | notify: restart_bor_service 161 | 162 | - name: "bor - Create inner chain data dir" 163 | ansible.builtin.file: 164 | path: "{{ bor_data_dir }}/bor/chaindata" 165 | state: directory 166 | owner: "{{ username }}" 167 | group: "{{ username }}" 168 | mode: "755" 169 | 170 | - name: "bor - Extract chain data" 171 | ansible.builtin.command: 172 | cmd: "tar -xf {{ quicksync_tmp_dir }}/{{ bor_snapshot_filename }}" 173 | chdir: "{{ bor_data_dir }}/bor/chaindata" 174 | async: 72000 175 | poll: 10 176 | become: true 177 | become_user: "{{ username }}" 178 | changed_when: false 179 | -------------------------------------------------------------------------------- /roles/polygon/templates/bor-config.toml.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | {% for section in final_bor_config %} 4 | [{{ section }}] 5 | {% for key, value in final_bor_config[section].items() %} 6 | {% if value is string %} 7 | {{ key }} = "{{ value }}" 8 | {% else %} 9 | {{ key }} = {{ value | to_json }} 10 | {% endif %} 11 | {% endfor %} 12 | 13 | {% endfor %} 14 | -------------------------------------------------------------------------------- /roles/polygon/templates/bor.service.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | [Unit] 4 | Description=Bor Daemon 5 | StartLimitIntervalSec=500 6 | StartLimitBurst=5 7 | After=network.target 8 | 9 | [Service] 10 | Type=simple 11 | User={{ username }} 12 | ExecStart={{ bin_dir }}/bor --config {{ bor_dir }}/config.toml --bootnodes "{{ bor_bootnodes }}" {{ bor_extra_run_args }} 13 | KillSignal=SIGINT 14 | TimeoutStopSec=120 15 | Restart=on-failure 16 | RestartSec=5s 17 | LimitNOFILE=65535 18 | Environment="VALIDATOR_ADDRESS={{ validator_address }}" 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /roles/polygon/templates/heimdalld-rest-server.service.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | [Unit] 4 | Description=Heimdall REST Server Daemon 5 | StartLimitIntervalSec=500 6 | StartLimitBurst=5 7 | After=network.target 8 | 9 | [Service] 10 | Type=simple 11 | User={{ username }} 12 | ExecStart={{ bin_dir }}/heimdalld rest-server --home {{ heimdall_dir }} --chain-id {{ network_id }} {{ heimdall_rest_server_extra_run_args }} 13 | Restart=on-failure 14 | RestartSec=5s 15 | LimitNOFILE=65535 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /roles/polygon/templates/heimdalld.service.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | [Unit] 4 | Description=Heimdall Daemon 5 | StartLimitIntervalSec=500 6 | StartLimitBurst=5 7 | After=network.target 8 | 9 | [Service] 10 | Type=simple 11 | User={{ username }} 12 | ExecStart={{ bin_dir }}/heimdalld start --home {{ heimdall_dir }} {{ heimdall_extra_run_args }} 13 | Restart=on-failure 14 | RestartSec=5s 15 | LimitNOFILE=65535 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /roles/tron/README.md: -------------------------------------------------------------------------------- 1 | # TRON role 2 | 3 | Ansible role to manage TRON blockchain node. 4 | 5 | ## Role Variables 6 | 7 | The role has default variables (see `defaults/main.yml`) which can be adjusted. 8 | 9 | ## Example Playbook 10 | 11 | By default, the role will use the [main_net_config.conf](https://github.com/tronprotocol/tron-deployment/blob/master/main_net_config.conf). 12 | But it can be replaced with the custom config. 13 | 14 | ```yaml 15 | - hosts: "all" 16 | gather_facts: true 17 | become: true 18 | 19 | roles: 20 | - role: lean_delivery.java 21 | java_distribution: corretto 22 | java_major_version: 8 23 | 24 | - role: trustwallet.blockchain.tron 25 | data_dir: /mnt/data # exampe of non-default data directory, default is /home/tron/.tron 26 | quicksync_mirror: oregon # options are oregon (default), frankfurt and singapore 27 | tron_config_override: ./config/main_net_config.conf # optional, set up a nodes with a new custom cofnig file 28 | ``` 29 | 30 | ## Development 31 | 32 | To aid in the development and testing of the Ansible role, we are 33 | using [Molecule](https://molecule.readthedocs.io/en/latest/index.html) roles testing framework. 34 | 35 | ```shell 36 | molecule -v test -s tron 37 | ``` 38 | 39 | 40 | ## References 41 | 42 | * [Trust Wallet](https://trustwallet.com) 43 | * [TRON GitHub](https://github.com/tronprotocol/java-tron) 44 | * [TRON Full Node Deployment](https://developers.tron.network/docs/deploy-the-fullnode-or-supernode) 45 | * [TRON Full Node API](https://developers.tron.network/reference/full-node-api-overview) 46 | 47 | ## License 48 | 49 | MIT 50 | -------------------------------------------------------------------------------- /roles/tron/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # is the non-root user under which daemon will operate 3 | username: tron 4 | 5 | # is a user home dir 6 | home_dir: "/home/{{ username }}" 7 | 8 | # is the tron binary version 9 | tron_version: 4.6.0 10 | 11 | # is the URL to download TRON FullNode.jar 12 | tron_jar_url: "https://github.com/tronprotocol/java-tron/releases/download/GreatVoyage-v{{ tron_version }}/FullNode.jar" 13 | 14 | # is the URL to check TRON FullNode.jar checksum 15 | tron_checksum_url: "https://github.com/tronprotocol/java-tron/releases/download/GreatVoyage-v{{ tron_version }}/sha256sum.txt" 16 | 17 | # is the tron data dir 18 | data_dir: "{{ home_dir }}/.tron" 19 | 20 | # is the chain main Java Archive file 21 | tron_jar: "{{ data_dir }}/FullNode.jar" 22 | 23 | # defines SystemD service name 24 | tron_daemon_name: trond 25 | 26 | # is the URL containing endoints to latest TRON state files 27 | quicksync_endpoints_json_url: https://backup.trongrid.io/get-files 28 | 29 | # options are oregon (default), frankfurt and singapore 30 | quicksync_mirror: oregon 31 | 32 | # is the maximum number of connections to the server for each download. 33 | quicksync_connection_num: 5 34 | 35 | # indicates whether chain backup sync is enabled. 36 | quicksync_enabled: true 37 | 38 | # skips steps to download quicksync chain data archive, 39 | # assuming that the file has been already downloaded. 40 | quicksync_skip_download: false 41 | 42 | # forces the chain data download step. 43 | # Warning, the content of the internal data dir will be wiped. 44 | # By default chain data will be downloaded only after initial install. 45 | quicksync_force: false 46 | 47 | # is the temporary dir to keep downloaded file. 48 | # The aria2c package is utilized to speed up download process, 49 | # but it requires double size to first download and then unpack data archive. 50 | # In cloud environment the download dir often points to temporary mounted drive. 51 | quicksync_tmp_dir: /tmp/quicksync 52 | 53 | # is an optional path to modified main_net_config.conf 54 | # tron_config_override: ./config/main_net_config.conf 55 | -------------------------------------------------------------------------------- /roles/tron/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Restart trond service" 3 | ansible.builtin.systemd: 4 | name: "{{ tron_daemon_name }}" 5 | state: restarted 6 | daemon_reload: true 7 | enabled: true 8 | listen: restart_service 9 | become: true 10 | -------------------------------------------------------------------------------- /roles/tron/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: vorotech 4 | description: Ansible role to manage TRON blockchain node 5 | company: Trust Wallet 6 | license: MIT 7 | min_ansible_version: 2.10.6 8 | platforms: 9 | - name: Ubuntu 10 | versions: 11 | - all 12 | galaxy_tags: 13 | - blockchain 14 | - tron 15 | dependencies: [] 16 | -------------------------------------------------------------------------------- /roles/tron/tasks/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Copy main net config" 3 | ansible.builtin.copy: 4 | content: "{{ lookup('file', tron_config_override | default('main_net_config.conf') ) }}" 5 | dest: "{{ data_dir }}/main_net_config.conf" 6 | owner: "{{ username }}" 7 | mode: "644" 8 | notify: restart_service 9 | become: true 10 | -------------------------------------------------------------------------------- /roles/tron/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Install apt packages" 3 | ansible.builtin.apt: 4 | update_cache: true 5 | cache_valid_time: 86400 6 | pkg: libgoogle-perftools4 7 | become: true 8 | 9 | - name: "Create user {{ username }}" 10 | ansible.builtin.user: 11 | name: "{{ username }}" 12 | become: true 13 | 14 | - name: "Calculate heap size" 15 | set_fact: 16 | tron_heap_size: "{{ (ansible_memtotal_mb / 1024 * 0.8) | round(0, 'floor') | int }}" 17 | 18 | - name: "Create systemd service file" 19 | ansible.builtin.template: 20 | src: templates/service.j2 21 | dest: "/etc/systemd/system/{{ tron_daemon_name }}.service" 22 | mode: "644" 23 | notify: restart_service 24 | become: true 25 | 26 | - name: "Ensure creation of data dir" 27 | ansible.builtin.file: 28 | dest: "{{ data_dir }}" 29 | state: directory 30 | owner: "{{ username }}" 31 | mode: "774" 32 | become: true 33 | 34 | - name: "Download FullNode.jar" 35 | ansible.builtin.get_url: 36 | url: "{{ tron_jar_url }}" 37 | checksum: "sha256:{{ tron_checksum_url }}" 38 | force: true 39 | dest: "{{ tron_jar }}" 40 | owner: "{{ username }}" 41 | mode: "755" 42 | notify: restart_service 43 | become: true 44 | -------------------------------------------------------------------------------- /roles/tron/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: install.yml 3 | - include_tasks: config.yml 4 | - include_tasks: sync.yml 5 | -------------------------------------------------------------------------------- /roles/tron/tasks/sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://stackoverflow.com/a/56379678/166473 3 | - name: "Install acl package" 4 | ansible.builtin.apt: 5 | update_cache: true 6 | cache_valid_time: 86400 7 | pkg: acl 8 | become: true 9 | 10 | - name: "Check TRON data directory was initialised" 11 | ansible.builtin.stat: 12 | path: "{{ data_dir }}/output-directory" 13 | register: tron_data_stat 14 | become: true 15 | 16 | - name: "Download chain data" 17 | when: 18 | - not tron_data_stat.stat.exists or quicksync_force 19 | - quicksync_enabled 20 | block: 21 | - name: "Install apt packages" 22 | ansible.builtin.apt: 23 | pkg: aria2 24 | become: true 25 | 26 | - name: "Get download target" # noqa command-instead-of-module 27 | ansible.builtin.command: 28 | cmd: "curl {{ quicksync_endpoints_json_url }}" 29 | changed_when: false 30 | register: quicksync_targets_result 31 | 32 | - name: "Set quicksync facts" 33 | ansible.builtin.set_fact: 34 | quicksync_targets: "{{ quicksync_targets_result.stdout | from_json }}" 35 | 36 | - name: "Set target url and filename" 37 | ansible.builtin.set_fact: 38 | target_url: "{{ quicksync_targets[quicksync_mirror]['endpoint'] }}" 39 | target_filename: "{{ quicksync_targets[quicksync_mirror]['files'][0]['name'][0] }}" 40 | 41 | - name: "Ensure download dir permissions" 42 | ansible.builtin.file: 43 | dest: "{{ quicksync_tmp_dir }}" 44 | state: directory 45 | mode: "777" 46 | 47 | - name: "Find downloaded data archive (if skip download)" 48 | ansible.builtin.find: 49 | paths: "{{ quicksync_tmp_dir }}" 50 | patterns: "*.tgz" 51 | register: local_data 52 | when: quicksync_skip_download 53 | 54 | - name: "Override target filename (if skip download)" 55 | ansible.builtin.set_fact: 56 | target_filename: "{{ local_data.files[0] }}" 57 | when: 58 | - quicksync_skip_download 59 | - local_data.files | length > 0 60 | 61 | - name: "Download chain data archive" 62 | ansible.builtin.shell: 63 | cmd: "aria2c --continue --max-connection-per-server={{ quicksync_connection_num }} {{ target_url }}/{{ target_filename }} > aria.log" 64 | chdir: "{{ quicksync_tmp_dir }}" 65 | async: 72000 66 | poll: 10 67 | when: not quicksync_skip_download 68 | 69 | - name: "Stop service (if running)" 70 | ansible.builtin.systemd: 71 | name: "{{ tron_daemon_name }}" 72 | state: stopped 73 | become: true 74 | 75 | - name: "Delete inner data dir" 76 | ansible.builtin.file: 77 | path: "{{ data_dir }}/output-directory" 78 | state: absent 79 | notify: restart_service 80 | become: true 81 | 82 | - name: "Extract chain data" # noqa command-instead-of-module 83 | ansible.builtin.command: 84 | cmd: "tar -xf {{ quicksync_tmp_dir }}/{{ target_filename }}" 85 | chdir: "{{ data_dir }}" 86 | async: 72000 87 | poll: 10 88 | become: true 89 | become_user: "{{ username }}" 90 | changed_when: false 91 | -------------------------------------------------------------------------------- /roles/tron/templates/service.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | [Unit] 4 | Description=TRON Daemon 5 | After=network.target 6 | 7 | [Service] 8 | Type=simple 9 | User={{ username }} 10 | WorkingDirectory={{ data_dir }} 11 | ExecStart=java -Xmx{{ tron_heap_size }}g -XX:+UseConcMarkSweepGC -Dfastjson.parser.safeMode=true -jar {{ tron_jar }} -c main_net_config.conf 12 | Restart=on-failure 13 | RestartSec=3 14 | LimitNOFILE=65535 15 | Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libtcmalloc.so.4" 16 | Environment="TCMALLOC_RELEASE_RATE=10" 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | --------------------------------------------------------------------------------