├── .dockerignore ├── .github ├── dependabot.yml ├── issue_template.md ├── pull_request_template.md └── workflows │ ├── ansible │ └── update_edgenet.yaml │ ├── build.yml │ ├── ci.yml │ ├── cla.yml │ ├── deploy.devnet.eph.yml │ ├── deploy.devnet.yml │ ├── deploy.testnet.yml │ ├── deploy_edgenet.yaml │ ├── e2e.yaml │ ├── lint.yml │ ├── pandoras_box.yml │ ├── release.yml │ ├── security.yml │ └── test.yml ├── .gitignore ├── .gitmodules ├── .golangci.yml ├── .goreleaser.yml ├── CLA.md ├── Dockerfile.release ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── ValidatorGuide.md ├── archive ├── backup.go ├── backup_test.go ├── restore.go ├── restore_test.go └── types.go ├── blockchain ├── blockchain.go ├── blockchain_test.go ├── storage │ ├── keyvalue.go │ ├── leveldb │ │ ├── leveldb.go │ │ └── leveldb_test.go │ ├── memory │ │ ├── memory.go │ │ └── memory_test.go │ ├── storage.go │ ├── testing.go │ └── utils.go ├── subscription.go ├── subscription_test.go └── testing.go ├── chain ├── chain.go ├── chain_test.go ├── params.go └── params_test.go ├── codecov.yml ├── command ├── backup │ ├── backup.go │ ├── params.go │ └── result.go ├── cli_output.go ├── common.go ├── common_output.go ├── default.go ├── genesis │ ├── genesis.go │ ├── params.go │ ├── predeploy │ │ ├── genesis_predeploy.go │ │ ├── params.go │ │ └── result.go │ ├── result.go │ └── utils.go ├── helper │ └── helper.go ├── ibft │ ├── candidates │ │ ├── ibft_candidates.go │ │ └── result.go │ ├── helper │ │ └── vote.go │ ├── ibft.go │ ├── propose │ │ ├── ibft_propose.go │ │ ├── params.go │ │ └── result.go │ ├── quorum │ │ ├── ibft_quorum.go │ │ ├── params.go │ │ └── result.go │ ├── snapshot │ │ ├── ibft_snapshot.go │ │ ├── params.go │ │ └── result.go │ ├── status │ │ ├── ibft_status.go │ │ └── result.go │ └── switch │ │ ├── ibft_switch.go │ │ ├── params.go │ │ └── result.go ├── json_output.go ├── license │ ├── license.go │ └── result.go ├── monitor │ ├── monitor.go │ └── result.go ├── output.go ├── peers │ ├── add │ │ ├── params.go │ │ ├── peers_add.go │ │ └── result.go │ ├── list │ │ ├── peers_list.go │ │ └── result.go │ ├── peers.go │ └── status │ │ ├── params.go │ │ ├── peers_status.go │ │ └── result.go ├── root │ └── root.go ├── secrets │ ├── generate │ │ ├── params.go │ │ ├── result.go │ │ └── secrets_generate.go │ ├── init │ │ ├── params.go │ │ ├── result.go │ │ └── secrets_init.go │ ├── output │ │ ├── params.go │ │ ├── result.go │ │ └── secrets_output.go │ └── secrets.go ├── server │ ├── config │ │ └── config.go │ ├── export │ │ ├── export.go │ │ ├── params.go │ │ └── result.go │ ├── init.go │ ├── params.go │ └── server.go ├── status │ ├── result.go │ └── status.go ├── txpool │ ├── status │ │ ├── result.go │ │ └── txpool_status.go │ ├── subscribe │ │ ├── params.go │ │ ├── result.go │ │ └── txpool_subscribe.go │ └── txpool.go ├── version │ ├── result.go │ └── version.go └── whitelist │ ├── deployment │ ├── deployment.go │ ├── params.go │ └── result.go │ ├── show │ ├── params.go │ ├── result.go │ └── show.go │ └── whitelist.go ├── consensus ├── consensus.go ├── dev │ └── dev.go ├── dummy │ └── dummy.go ├── ibft │ ├── consensus.go │ ├── consensus_backend.go │ ├── consensus_backend_test.go │ ├── fork │ │ ├── fork.go │ │ ├── fork_test.go │ │ ├── helper.go │ │ ├── helper_test.go │ │ ├── hookregister.go │ │ ├── hooks.go │ │ ├── hooks_test.go │ │ ├── manager.go │ │ ├── manager_test.go │ │ ├── storewrapper.go │ │ ├── storewrapper_test.go │ │ ├── type.go │ │ └── type_test.go │ ├── helper_test.go │ ├── hook │ │ ├── hook.go │ │ └── hook_test.go │ ├── ibft.go │ ├── messages.go │ ├── operator_service.go │ ├── proto │ │ ├── ibft_operator.pb.go │ │ ├── ibft_operator.proto │ │ └── ibft_operator_grpc.pb.go │ ├── sign_test.go │ ├── signer │ │ ├── bls.go │ │ ├── bls_test.go │ │ ├── ecdsa.go │ │ ├── ecdsa_test.go │ │ ├── extra.go │ │ ├── extra_test.go │ │ ├── helper.go │ │ ├── helper_test.go │ │ ├── key_manager.go │ │ ├── mock_test.go │ │ ├── signer.go │ │ └── signer_test.go │ ├── state_test.go │ ├── transport.go │ ├── validators.go │ └── verifier.go └── utils.go ├── contracts ├── abis │ ├── abis.go │ └── json.go └── staking │ ├── query.go │ └── query_test.go ├── crypto ├── crypto.go ├── crypto_test.go ├── txsigner.go └── txsigner_test.go ├── docker └── local │ ├── Dockerfile │ ├── docker-compose.yml │ └── exzocoin.sh ├── e2e ├── README.md ├── broadcast_test.go ├── const.go ├── discovery_test.go ├── framework │ ├── config.go │ ├── helper.go │ ├── ibft.go │ └── testserver.go ├── genesis_test.go ├── ibft_test.go ├── jsonrpc_test.go ├── logs_test.go ├── metadata │ ├── StressTest.sol │ ├── predeploy.sol │ ├── predeploy_abi.json │ └── sample.sol ├── pos_poa_switch_test.go ├── pos_test.go ├── syncer_test.go ├── transaction_test.go ├── txpool_test.go └── websocket_test.go ├── generate_dependency_licenses.sh ├── go.mod ├── go.sum ├── helper ├── common │ └── common.go ├── config │ └── config.go ├── enode │ ├── enode.go │ └── enode_test.go ├── hex │ ├── hex.go │ └── hex_test.go ├── ipc │ ├── ipc_unix.go │ └── ipc_windows.go ├── keccak │ ├── keccak.go │ ├── keccak_test.go │ └── pool.go ├── keystore │ └── keystore.go ├── predeployment │ ├── argparser.go │ ├── argparser_test.go │ └── predeployment.go ├── progress │ └── chain.go ├── staking │ └── staking.go └── tests │ ├── assert.go │ └── testing.go ├── jsonrpc ├── codec.go ├── codec_test.go ├── debug_endpoint.go ├── debug_endpoint_test.go ├── dispatcher.go ├── dispatcher_test.go ├── errors.go ├── eth_blockchain_test.go ├── eth_endpoint.go ├── eth_endpoint_test.go ├── eth_state_test.go ├── eth_txpool_test.go ├── filter_manager.go ├── filter_manager_test.go ├── helper.go ├── helper_test.go ├── jsonrpc.go ├── jsonrpc_test.go ├── mocks_test.go ├── net_endpoint.go ├── net_endpoint_test.go ├── query.go ├── query_test.go ├── testsuite │ ├── block-empty.json │ ├── block-with-txn-bodies.json │ ├── block-with-txn-hashes.json │ ├── transaction-pending.json │ └── transaction-sealed.json ├── txpool_endpoint.go ├── txpool_endpoint_test.go ├── types.go ├── types_test.go ├── web3_endpoint.go └── web3_endpoint_test.go ├── licenses ├── bsd_licenses.json └── licenses.go ├── main.go ├── mainnet-genesis.json ├── network ├── bootnodes.go ├── common │ └── common.go ├── config.go ├── connections.go ├── dial │ ├── dial_queue.go │ ├── dial_queue_test.go │ ├── dial_task.go │ └── heap.go ├── discovery │ ├── discovery.go │ └── discovery_test.go ├── discovery_e2e_test.go ├── e2e_testing.go ├── event │ └── peer_event.go ├── gossip.go ├── gossip_test.go ├── grpc │ └── grpc.go ├── identity │ ├── identity.go │ └── identity_test.go ├── identity_e2e_test.go ├── keystore.go ├── proto │ ├── discovery.pb.go │ ├── discovery.proto │ ├── discovery_grpc.pb.go │ ├── identity.pb.go │ ├── identity.proto │ ├── identity_grpc.pb.go │ ├── testing.pb.go │ ├── testing.proto │ └── testing_grpc.pb.go ├── server.go ├── server_discovery.go ├── server_identity.go ├── server_test.go └── testing │ └── testing.go ├── secrets ├── awsssm │ └── aws_ssm.go ├── config.go ├── gcpssm │ └── gcp_ssm.go ├── hashicorpvault │ └── hashicorp_vault.go ├── helper │ └── helper.go ├── local │ ├── local.go │ └── local_test.go ├── secrets.go └── secrets_test.go ├── server ├── builtin.go ├── config.go ├── proto │ ├── system.pb.go │ ├── system.proto │ └── system_grpc.pb.go ├── server.go ├── server_metrics.go └── system_service.go ├── state ├── executor.go ├── immutable-trie │ ├── encoding.go │ ├── encoding_test.go │ ├── hasher.go │ ├── snapshot.go │ ├── state.go │ ├── state_test.go │ ├── storage.go │ └── trie.go ├── runtime │ ├── evm │ │ ├── bitmap.go │ │ ├── bitmap_test.go │ │ ├── dispatch_table.go │ │ ├── dispatch_table_test.go │ │ ├── evm.go │ │ ├── evm_test.go │ │ ├── instructions.go │ │ ├── instructions_test.go │ │ ├── opcodes.go │ │ ├── opcodes_test.go │ │ ├── state.go │ │ └── state_test.go │ ├── precompiled │ │ ├── base.go │ │ ├── base_test.go │ │ ├── blake2f.go │ │ ├── blake2f_test.go │ │ ├── bn256.go │ │ ├── bn256_test.go │ │ ├── fixtures │ │ │ └── blake2f.json │ │ ├── modexp.go │ │ ├── modexp_test.go │ │ ├── precompiled.go │ │ └── testing.go │ ├── runtime.go │ └── tracer │ │ ├── structtracer │ │ ├── tracer.go │ │ └── tracer_test.go │ │ └── types.go ├── state.go ├── testing.go ├── transition_test.go ├── txn.go └── txn_test.go ├── syncer ├── client.go ├── client_test.go ├── peers.go ├── peers_test.go ├── proto │ ├── syncer.pb.go │ ├── syncer.proto │ └── syncer_grpc.pb.go ├── service.go ├── service_test.go ├── syncer.go ├── syncer_test.go └── types.go ├── testnet-genesis.json ├── tests ├── evm_test.go ├── state_test.go └── testing.go ├── txpool ├── account.go ├── event_manager.go ├── event_manager_test.go ├── event_queue.go ├── event_subscription.go ├── event_subscription_test.go ├── lookup_map.go ├── mock_test.go ├── operator.go ├── proto │ ├── operator.pb.go │ ├── operator.proto │ ├── operator_grpc.pb.go │ ├── v1.pb.go │ └── v1.proto ├── query.go ├── queue.go ├── slot_gauge.go ├── txpool.go └── txpool_test.go ├── types ├── buildroot │ ├── buildroot.go │ ├── buildroot_fast.go │ └── buildroot_fast_test.go ├── encoding.go ├── header.go ├── header_hash.go ├── receipt.go ├── rlp_encoding_test.go ├── rlp_marshal.go ├── rlp_marshal_storage.go ├── rlp_unmarshal.go ├── rlp_unmarshal_storage.go ├── transaction.go ├── types.go └── types_test.go ├── validators ├── bls.go ├── bls_test.go ├── ecdsa.go ├── ecdsa_test.go ├── helper.go ├── helper_test.go ├── json_test.go ├── set.go ├── set_test.go ├── store │ ├── contract │ │ ├── contract.go │ │ ├── contract_test.go │ │ ├── fetcher.go │ │ └── fetcher_test.go │ ├── snapshot │ │ ├── helper.go │ │ ├── helper_test.go │ │ ├── snapshot.go │ │ ├── snapshot_test.go │ │ ├── types.go │ │ └── types_test.go │ ├── test_helper.go │ ├── types.go │ └── types_test.go ├── types.go └── types_test.go └── versioning └── versioning.go /.dockerignore: -------------------------------------------------------------------------------- 1 | docker/local/Dockerfile 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: / 5 | target-branch: "develop" 6 | schedule: 7 | interval: weekly 8 | ignore: 9 | - dependency-name: "github.com/aws/aws-sdk-go" 10 | update-types: [ "version-update:semver-patch" ] 11 | open-pull-requests-limit: 10 12 | pull-request-branch-name: 13 | separator: "-" 14 | reviewers: 15 | - "lazartravica" 16 | - "Kourin1996" 17 | - "zivkovicmilos" 18 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | # [ Subject of the issue ] 2 | ## Description 3 | Describe your issue in as much detail as possible here. 4 | 5 | ## Your environment 6 | - OS and version. 7 | - The version of the Polygon Edge. 8 | (*Confirm the version of your Polygon edge client by running the following command: `exzocoin version --grpc-address GRPC_ADDRESS`*) 9 | - The branch that causes this issue. 10 | - Locally or Cloud hosted (which provider). 11 | - Please confirm if the validators are running under containerized environment (K8s, Docker, etc.). 12 | 13 | ## Steps to reproduce 14 | - Tell us how to reproduce this issue. 15 | - Where the issue is, if you know. 16 | - Which commands triggered the issue, if any. 17 | - Provide us with the content of your genesis file. 18 | - Provide us with commands that you used to start your validators. 19 | - Provide us with the peer list of each of your validators by running the following command: `exzocoin peers list --grpc-address GRPC_ADDRESS`. 20 | - Is the chain producing blocks and serving customers atm? 21 | 22 | ## Expected behavior 23 | - Tell us what should happen. 24 | - Tell us what happened instead. 25 | 26 | ## Logs 27 | Provide us with debug logs from all of your validators by setting logging to `debug` output with: `server --log-level debug` 28 | 29 | ## Proposed solution 30 | If you have an idea on how to fix this issue, please write it down here, so we can begin discussing it 31 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please provide a detailed description of what was done in this PR 4 | 5 | # Changes include 6 | 7 | - [ ] Bugfix (non-breaking change that solves an issue) 8 | - [ ] Hotfix (change that solves an urgent issue, and requires immediate attention) 9 | - [ ] New feature (non-breaking change that adds functionality) 10 | - [ ] Breaking change (change that is not backwards-compatible and/or changes current functionality) 11 | 12 | # Breaking changes 13 | 14 | Please complete this section if any breaking changes have been made, otherwise delete it 15 | 16 | # Checklist 17 | 18 | - [ ] I have assigned this PR to myself 19 | - [ ] I have added at least 1 reviewer 20 | - [ ] I have added the relevant labels 21 | - [ ] I have updated the official documentation 22 | - [ ] I have added sufficient documentation in code 23 | 24 | ## Testing 25 | 26 | - [ ] I have tested this code with the official test suite 27 | - [ ] I have tested this code manually 28 | 29 | ### Manual tests 30 | 31 | Please complete this section if you ran manual tests for this functionality, otherwise delete it 32 | 33 | # Documentation update 34 | 35 | Please link the documentation update PR in this section if it's present, otherwise delete it 36 | 37 | # Additional comments 38 | 39 | Please post additional comments in this section if you have them, otherwise delete it 40 | -------------------------------------------------------------------------------- /.github/workflows/ansible/update_edgenet.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | name: Update Polygon Edge binary 4 | hosts: 5 | - all 6 | become: yes 7 | tasks: 8 | ## update & upgrade system 9 | - name: Update & upgrade system 10 | apt: 11 | upgrade: yes 12 | update_cache: yes 13 | 14 | ## stop exzocoin service 15 | - name: Stop polygon edge service 16 | systemd: 17 | state: stopped 18 | name: exzocoin 19 | 20 | ## get the latest release 21 | - name: Get latest release link 22 | uri: 23 | url: https://api.github.com/repos/ExzoNetwork/ExzoCoin/releases/latest 24 | return_content: true 25 | register: edge_release 26 | 27 | ## download the latest release 28 | - name: Download latest Polygon Edge release 29 | get_url: 30 | url: "{{ edge_release.json.assets[3].browser_download_url }}" 31 | dest: /tmp/exzocoin.tar.gz 32 | force: yes 33 | 34 | ## create temp dir for release 35 | - name: Create temp dir for Polygon Edge release 36 | file: 37 | path: /tmp/exzocoin 38 | state: directory 39 | 40 | ## unpack release tar 41 | - name: Unpack Polygon Edge release 42 | unarchive: 43 | remote_src: yes 44 | src: /tmp/exzocoin.tar.gz 45 | dest: /tmp/exzocoin 46 | 47 | ## set exzocoin to PATH 48 | - name: Place Polygon Edge binary to PATH 49 | copy: 50 | remote_src: yes 51 | src: /tmp/exzocoin/exzocoin 52 | dest: /usr/local/bin/ 53 | mode: a+x 54 | force: yes 55 | 56 | ## remove release temp dir 57 | - name: Remove temp Polygon Edge release dir 58 | file: 59 | state: absent 60 | path: /tmp/exzocoin 61 | 62 | ## start polygon edge service 63 | - name: Start exzocoin service 64 | systemd: 65 | state: restarted 66 | name: exzocoin 67 | daemon_reload: yes 68 | enabled: yes -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Build 3 | on: # yamllint disable-line rule:truthy 4 | workflow_dispatch: 5 | workflow_call: 6 | 7 | jobs: 8 | go_build: 9 | name: Polygon Edge 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v3 14 | - name: Setup Go environment 15 | uses: actions/setup-go@v3.3.0 16 | with: 17 | go-version: 1.18.x 18 | 19 | - name: Build Polygon Edge 20 | run: go build -tags netgo -ldflags="-s -w -linkmode external -extldflags "-static" -X \"github.com/ExzoNetwork/ExzoCoin/versioning.Version=${GITHUB_REF_NAME}\" -X \"github.com/ExzoNetwork/ExzoCoin/versioning.Commit=${GITHUB_SHA}\"" && tar -czvf exzocoin.tar.gz exzocoin 21 | env: 22 | CC: gcc 23 | CXX: g++ 24 | GOARC: amd64 25 | GOOS: linux 26 | 27 | - name: 'Upload Artifact' 28 | uses: actions/upload-artifact@v3 29 | with: 30 | name: exzocoin 31 | path: exzocoin.tar.gz 32 | retention-days: 3 33 | 34 | go_build_reproducibility: 35 | name: Verify Build Reproducibility 36 | runs-on: ubuntu-latest 37 | continue-on-error: true 38 | steps: 39 | - name: Checkout code 40 | uses: actions/checkout@v3 41 | - name: Setup Go environment 42 | uses: actions/setup-go@v3.3.0 43 | with: 44 | go-version: 1.18.x 45 | 46 | - name: 'Reproduce builds' 47 | continue-on-error: true 48 | run: | 49 | go build -o ./edge-1 -trimpath -buildvcs=false 50 | go build -o ./edge-2 -trimpath -buildvcs=false 51 | 52 | buildsha1=$(shasum -a256 ./edge-1 | awk '{print $1}') 53 | buildsha2=$(shasum -a256 ./edge-2 | awk '{print $1}') 54 | 55 | echo "Build 1 SHA: $buildsha1" 56 | echo "Build 2 SHA: $buildsha2" 57 | 58 | if [ "$buildsha1" != "$buildsha2" ]; then 59 | echo "Build artifact does not match original" 60 | exit 1 61 | else 62 | echo "Build artifact matches original" 63 | fi 64 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Pull Request CI 3 | on: # yamllint disable-line rule:truthy 4 | workflow_dispatch: {} 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | name: Build 10 | uses: ./.github/workflows/build.yml 11 | 12 | test: 13 | name: Test 14 | uses: ./.github/workflows/test.yml 15 | needs: build 16 | -------------------------------------------------------------------------------- /.github/workflows/cla.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "CLA Assistant" 3 | on: # yamllint disable-line rule:truthy 4 | issue_comment: 5 | types: 6 | - created 7 | pull_request_target: 8 | types: 9 | - opened 10 | - closed 11 | - synchronize 12 | 13 | jobs: 14 | CLAssistant: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: "CLA Assistant" 18 | if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' 19 | # Beta Release 20 | uses: cla-assistant/github-action@v2.1.3-beta 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | # the below token should have repo scope and must be manually added by you in the repository's secret 24 | PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 25 | with: 26 | path-to-signatures: 'cla.json' 27 | path-to-document: 'https://github.com/ExzoNetwork/ExzoCoin/blob/develop/CLA.md' 28 | branch: 'cla-signatures' 29 | allowlist: dependabot[bot],dependabot-preview[bot] 30 | -------------------------------------------------------------------------------- /.github/workflows/deploy_edgenet.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | on: # yamllint disable-line rule:truthy 3 | release: 4 | types: 5 | - published 6 | 7 | jobs: 8 | deploy_to_edge_net: 9 | name: Update EdgeNet 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Get aws-commander 13 | run: | 14 | wget https://github.com/Trapesys/aws-commander/releases/download/v0.2.0/aws-commander_0.2.0_Linux_x86_64.tar.gz 15 | tar -xf aws-commander_0.2.0_Linux_x86_64.tar.gz 16 | sudo mv aws-commander /usr/local/bin 17 | 18 | - name: Checkout code 19 | uses: actions/checkout@v3 20 | 21 | - name: Run deployment Ansible 22 | env: 23 | AWS_ACCESS_KEY_ID: ${{ secrets.EDGENET_AWS_ACCESS_KEY_ID }} 24 | AWS_SECRET_ACCESS_KEY: ${{ secrets.EDGENET_AWS_SECRET_ACCESS_KEY }} 25 | run: > 26 | /usr/local/bin/aws-commander 27 | -instances i-039f7c0b3328a00f8,i-035b9f2d78cfb8ea9,i-00a6c7cb3a213f21f,i-03ac2f42ddcba6120 28 | -mode ansible 29 | -playbook .github/workflows/ansible/update_edgenet.yaml 30 | -aws-zone us-west-2 31 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: E2E tests 3 | on: # yamllint disable-line rule:truthy 4 | push: 5 | branches: 6 | - main 7 | - develop 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | env: 14 | E2E_TESTS: true 15 | E2E_LOGS: true 16 | CI_VERBOSE: true 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Install Go 20 | uses: actions/setup-go@v3 21 | with: 22 | go-version: 1.18.x 23 | - name: Run tests 24 | run: make test-e2e 25 | - name: Archive test logs 26 | if: always() 27 | uses: actions/upload-artifact@v3 28 | with: 29 | name: e2e-logs 30 | path: e2e-logs-*/ 31 | retention-days: 30 32 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lint 3 | on: # yamllint disable-line rule:truthy 4 | push: 5 | branches-ignore: 6 | - 'develop' 7 | - 'release/**' 8 | tags-ignore: 9 | - 'v*' 10 | paths: 11 | - '**.go' 12 | workflow_call: {} 13 | workflow_dispatch: {} 14 | 15 | jobs: 16 | golangci_lint: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Install Go 20 | uses: actions/setup-go@v3 21 | with: 22 | go-version: 1.18.x 23 | 24 | - name: Checkout code 25 | uses: actions/checkout@v3 26 | 27 | - name: Lint 28 | uses: golangci/golangci-lint-action@v3 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | on: # yamllint disable-line rule:truthy 4 | push: 5 | branches-ignore: 6 | - '**' 7 | tags: 8 | - 'v*.*.*' 9 | # to be used by fork patch-releases ^^ 10 | - 'v*.*.*-*' 11 | 12 | jobs: 13 | goreleaser: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: Set up Go 22 | uses: actions/setup-go@master 23 | with: 24 | go-version: 1.18.x 25 | 26 | - name: Prepare 27 | id: prepare 28 | run: | 29 | TAG=${GITHUB_REF#refs/tags/} 30 | echo ::set-output name=tag_name::${TAG} 31 | - name: Set up QEMU 32 | uses: docker/setup-qemu-action@v1 33 | 34 | - name: Run GoReleaser 35 | run: | 36 | docker run \ 37 | --rm \ 38 | --privileged \ 39 | -e CGO_ENABLED=1 \ 40 | -e GITHUB_TOKEN \ 41 | -e DOCKER_USERNAME \ 42 | -e DOCKER_PASSWORD \ 43 | -e SLACK_WEBHOOK \ 44 | -v /var/run/docker.sock:/var/run/docker.sock \ 45 | -v `pwd`:/go/src/$(PACKAGE_NAME) \ 46 | -w /go/src/$(PACKAGE_NAME) \ 47 | ghcr.io/goreleaser/goreleaser-cross:${GOLANG_CROSS_VERSION} \ 48 | --rm-dist --skip-validate 49 | env: 50 | PACKAGE_NAME: github.com/ExzoNetwork/ExzoCoin 51 | GOLANG_CROSS_VERSION: v1.18.3 52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 53 | VERSION: ${{ steps.prepare.outputs.tag_name }} 54 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} 55 | SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }} 56 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} 57 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} 58 | -------------------------------------------------------------------------------- /.github/workflows/security.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Security Scan 3 | on: # yamllint disable-line rule:truthy 4 | workflow_call: 5 | secrets: 6 | SNYK_TOKEN: 7 | required: true 8 | SNYK_ORG: 9 | required: true 10 | workflow_dispatch: {} 11 | schedule: 12 | - cron: '0 0 * * 0' 13 | 14 | jobs: 15 | snyk: 16 | name: Snyk and Publish 17 | runs-on: ubuntu-latest 18 | continue-on-error: true 19 | steps: 20 | - name: Checkout Source 21 | uses: actions/checkout@master 22 | - name: Run Snyk to check for vulnerabilities 23 | uses: snyk/actions/golang@master 24 | continue-on-error: true 25 | env: 26 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} 27 | with: 28 | args: --org=${{ secrets.SNYK_ORG }} --sarif-file-output=snyk.sarif 29 | - name: Upload result to GitHub Code Scanning 30 | uses: github/codeql-action/upload-sarif@v2 31 | with: 32 | sarif_file: snyk.sarif 33 | snyk-code: 34 | name: Snyk Code and Publish 35 | runs-on: ubuntu-latest 36 | continue-on-error: true 37 | steps: 38 | - name: Checkout Source 39 | uses: actions/checkout@master 40 | - name: Run Snyk SAST to check for vulnerabilities 41 | uses: snyk/actions/golang@master 42 | continue-on-error: true 43 | env: 44 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} 45 | with: 46 | args: --org=${{ secrets.SNYK_ORG }} --sarif-file-output=snyk.sarif 47 | command: code test 48 | - name: Upload result to GitHub Code Scanning 49 | uses: github/codeql-action/upload-sarif@v2 50 | with: 51 | sarif_file: snyk.sarif 52 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test 3 | on: # yamllint disable-line rule:truthy 4 | workflow_dispatch: 5 | workflow_call: 6 | 7 | jobs: 8 | go_test: 9 | name: Polygon Edge 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Setup Go 13 | uses: actions/setup-go@v3 14 | with: 15 | go-version: 1.18.x 16 | 17 | - name: Checkout Code 18 | uses: actions/checkout@v3 19 | with: 20 | submodules: recursive 21 | 22 | - name: Run Go Test 23 | run: go test -coverprofile coverage.out -timeout 20m `go list ./... | grep -v e2e` 24 | 25 | - name: Upload coverage file to Codecov 26 | uses: codecov/codecov-action@v3 27 | with: 28 | files: coverage.out 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | peers.json 2 | genesis.json 3 | secretsManagerConfig.json 4 | *-packr.go 5 | config*.json 6 | 7 | bin/ 8 | 9 | test-chain* 10 | exzocoin-chain* 11 | .idea 12 | 13 | # Exclude the build .exe file from version control 14 | dist/ 15 | main.exe 16 | main 17 | 18 | # MacOS Leftovers 19 | .DS_Store 20 | .vscode 21 | 22 | # exclude build folder 23 | artifacts 24 | 25 | # Log files 26 | *.log 27 | 28 | vendor/ 29 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/tests"] 2 | path = tests/tests 3 | url = https://github.com/ethereum/tests.git 4 | -------------------------------------------------------------------------------- /Dockerfile.release: -------------------------------------------------------------------------------- 1 | FROM alpine:3.14 2 | 3 | RUN set -x \ 4 | && apk add --update --no-cache \ 5 | ca-certificates \ 6 | && rm -rf /var/cache/apk/* 7 | COPY exzocoin /usr/local/bin/ 8 | 9 | EXPOSE 8545 9632 1478 10 | ENTRYPOINT ["exzocoin"] 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: download-spec-tests 3 | download-spec-tests: 4 | git submodule init 5 | git submodule update 6 | 7 | .PHONY: bindata 8 | bindata: 9 | go-bindata -pkg chain -o ./chain/chain_bindata.go ./chain/chains 10 | 11 | .PHONY: protoc 12 | protoc: 13 | protoc --go_out=. --go-grpc_out=. ./server/proto/*.proto 14 | protoc --go_out=. --go-grpc_out=. ./protocol/proto/*.proto 15 | protoc --go_out=. --go-grpc_out=. ./network/proto/*.proto 16 | protoc --go_out=. --go-grpc_out=. ./txpool/proto/*.proto 17 | protoc --go_out=. --go-grpc_out=. ./consensus/ibft/**/*.proto 18 | 19 | .PHONY: build 20 | build: 21 | $(eval LATEST_VERSION = $(shell git describe --tags --abbrev=0)) 22 | $(eval COMMIT_HASH = $(shell git rev-parse HEAD)) 23 | $(eval BRANCH = $(shell git rev-parse --abbrev-ref HEAD | tr -d '\040\011\012\015\n')) 24 | $(eval TIME = $(shell date)) 25 | go build -o exzocoin -ldflags="\ 26 | -X 'github.com/ExzoNetwork/ExzoCoin/versioning.Version=$(LATEST_VERSION)' \ 27 | -X 'github.com/ExzoNetwork/ExzoCoin/versioning.Commit=$(COMMIT_HASH)'\ 28 | -X 'github.com/ExzoNetwork/ExzoCoin/versioning.Branch=$(BRANCH)'\ 29 | -X 'github.com/ExzoNetwork/ExzoCoin/versioning.BuildTime=$(TIME)'" \ 30 | main.go 31 | 32 | .PHONY: lint 33 | lint: 34 | golangci-lint run --config .golangci.yml 35 | 36 | .PHONY: generate-bsd-licenses 37 | generate-bsd-licenses: 38 | ./generate_dependency_licenses.sh BSD-3-Clause,BSD-2-Clause > ./licenses/bsd_licenses.json 39 | 40 | .PHONY: test 41 | test: 42 | go test -timeout=20m `go list ./... | grep -v e2e` 43 | 44 | .PHONY: test-e2e 45 | test-e2e: 46 | # We need to build the binary with the race flag enabled 47 | # because it will get picked up and run during e2e tests 48 | # and the e2e tests should error out if any kind of race is found 49 | go build -race -o artifacts/exzocoin . 50 | env EDGE_BINARY=${PWD}/artifacts/exzocoin go test -v -timeout=30m ./e2e/... 51 | 52 | .PHONY: run-local 53 | run-local: 54 | docker-compose -f ./docker/local/docker-compose.yml up -d --build 55 | 56 | .PHONY: stop-local 57 | stop-local: 58 | docker-compose -f ./docker/local/docker-compose.yml stop 59 | 60 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Polygon Technology Security Information 2 | 3 | ## Link to vulnerability disclosure details (Bug Bounty). 4 | - Websites and Applications: https://hackerone.com/polygon-technology 5 | - Smart Contracts: https://immunefi.com/bounty/polygon 6 | 7 | ## Languages that our team speaks and understands. 8 | Preferred-Languages: en 9 | 10 | ## Security-related job openings at Polygon. 11 | https://polygon.technology/careers 12 | 13 | ## Polygon security contact details. 14 | security@polygon.technology 15 | 16 | ## The URL for accessing the security.txt file. 17 | Canonical: https://polygon.technology/security.txt 18 | -------------------------------------------------------------------------------- /archive/types.go: -------------------------------------------------------------------------------- 1 | package archive 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/types" 7 | "github.com/umbracle/fastrlp" 8 | ) 9 | 10 | // Metadata is the data stored in the beginning of backup 11 | type Metadata struct { 12 | Latest uint64 13 | LatestHash types.Hash 14 | } 15 | 16 | // MarshalRLP returns RLP encoded bytes 17 | func (m *Metadata) MarshalRLP() []byte { 18 | return m.MarshalRLPTo(nil) 19 | } 20 | 21 | // MarshalRLPTo sets RLP encoded bytes to given byte slice 22 | func (m *Metadata) MarshalRLPTo(dst []byte) []byte { 23 | return types.MarshalRLPTo(m.MarshalRLPWith, dst) 24 | } 25 | 26 | // MarshalRLPWith appends own field into arena for encode 27 | func (m *Metadata) MarshalRLPWith(arena *fastrlp.Arena) *fastrlp.Value { 28 | vv := arena.NewArray() 29 | 30 | vv.Set(arena.NewUint(m.Latest)) 31 | vv.Set(arena.NewBytes(m.LatestHash.Bytes())) 32 | 33 | return vv 34 | } 35 | 36 | // UnmarshalRLP unmarshals and sets the fields from RLP encoded bytes 37 | func (m *Metadata) UnmarshalRLP(input []byte) error { 38 | return types.UnmarshalRlp(m.UnmarshalRLPFrom, input) 39 | } 40 | 41 | // UnmarshalRLPFrom sets the fields from parsed RLP encoded value 42 | func (m *Metadata) UnmarshalRLPFrom(p *fastrlp.Parser, v *fastrlp.Value) error { 43 | elems, err := v.GetElems() 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if len(elems) < 2 { 49 | return fmt.Errorf("incorrect number of elements to decode Metadata, expected 2 but found %d", len(elems)) 50 | } 51 | 52 | if m.Latest, err = elems[0].GetUint64(); err != nil { 53 | return err 54 | } 55 | 56 | if err = elems[1].GetHash(m.LatestHash[:]); err != nil { 57 | return err 58 | } 59 | 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /blockchain/storage/leveldb/leveldb.go: -------------------------------------------------------------------------------- 1 | package leveldb 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/blockchain/storage" 7 | "github.com/hashicorp/go-hclog" 8 | "github.com/syndtr/goleveldb/leveldb" 9 | ) 10 | 11 | // Factory creates a leveldb storage 12 | func Factory(config map[string]interface{}, logger hclog.Logger) (storage.Storage, error) { 13 | path, ok := config["path"] 14 | if !ok { 15 | return nil, fmt.Errorf("path not found") 16 | } 17 | 18 | pathStr, ok := path.(string) 19 | if !ok { 20 | return nil, fmt.Errorf("path is not a string") 21 | } 22 | 23 | return NewLevelDBStorage(pathStr, logger) 24 | } 25 | 26 | // NewLevelDBStorage creates the new storage reference with leveldb 27 | func NewLevelDBStorage(path string, logger hclog.Logger) (storage.Storage, error) { 28 | db, err := leveldb.OpenFile(path, nil) 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | kv := &levelDBKV{db} 34 | 35 | return storage.NewKeyValueStorage(logger.Named("leveldb"), kv), nil 36 | } 37 | 38 | // levelDBKV is the leveldb implementation of the kv storage 39 | type levelDBKV struct { 40 | db *leveldb.DB 41 | } 42 | 43 | // Set sets the key-value pair in leveldb storage 44 | func (l *levelDBKV) Set(p []byte, v []byte) error { 45 | return l.db.Put(p, v, nil) 46 | } 47 | 48 | // Get retrieves the key-value pair in leveldb storage 49 | func (l *levelDBKV) Get(p []byte) ([]byte, bool, error) { 50 | data, err := l.db.Get(p, nil) 51 | if err != nil { 52 | if err.Error() == "leveldb: not found" { 53 | return nil, false, nil 54 | } 55 | 56 | return nil, false, err 57 | } 58 | 59 | return data, true, nil 60 | } 61 | 62 | // Close closes the leveldb storage instance 63 | func (l *levelDBKV) Close() error { 64 | return l.db.Close() 65 | } 66 | -------------------------------------------------------------------------------- /blockchain/storage/leveldb/leveldb_test.go: -------------------------------------------------------------------------------- 1 | package leveldb 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/blockchain/storage" 8 | "github.com/hashicorp/go-hclog" 9 | ) 10 | 11 | func newStorage(t *testing.T) (storage.Storage, func()) { 12 | t.Helper() 13 | 14 | path, err := os.MkdirTemp("/tmp", "minimal_storage") 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | 19 | s, err := NewLevelDBStorage(path, hclog.NewNullLogger()) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | closeFn := func() { 25 | if err := s.Close(); err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | if err := os.RemoveAll(path); err != nil { 30 | t.Fatal(err) 31 | } 32 | } 33 | 34 | return s, closeFn 35 | } 36 | 37 | func TestStorage(t *testing.T) { 38 | storage.TestStorage(t, newStorage) 39 | } 40 | -------------------------------------------------------------------------------- /blockchain/storage/memory/memory.go: -------------------------------------------------------------------------------- 1 | package memory 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/blockchain/storage" 5 | "github.com/ExzoNetwork/ExzoCoin/helper/hex" 6 | "github.com/hashicorp/go-hclog" 7 | ) 8 | 9 | // NewMemoryStorage creates the new storage reference with inmemory 10 | func NewMemoryStorage(logger hclog.Logger) (storage.Storage, error) { 11 | db := &memoryKV{map[string][]byte{}} 12 | 13 | return storage.NewKeyValueStorage(logger, db), nil 14 | } 15 | 16 | // memoryKV is an in memory implementation of the kv storage 17 | type memoryKV struct { 18 | db map[string][]byte 19 | } 20 | 21 | func (m *memoryKV) Set(p []byte, v []byte) error { 22 | m.db[hex.EncodeToHex(p)] = v 23 | 24 | return nil 25 | } 26 | 27 | func (m *memoryKV) Get(p []byte) ([]byte, bool, error) { 28 | v, ok := m.db[hex.EncodeToHex(p)] 29 | if !ok { 30 | return nil, false, nil 31 | } 32 | 33 | return v, true, nil 34 | } 35 | 36 | func (m *memoryKV) Close() error { 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /blockchain/storage/memory/memory_test.go: -------------------------------------------------------------------------------- 1 | package memory 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/blockchain/storage" 7 | ) 8 | 9 | func TestStorage(t *testing.T) { 10 | t.Helper() 11 | 12 | f := func(t *testing.T) (storage.Storage, func()) { 13 | t.Helper() 14 | 15 | s, _ := NewMemoryStorage(nil) 16 | 17 | return s, func() {} 18 | } 19 | storage.TestStorage(t, f) 20 | } 21 | -------------------------------------------------------------------------------- /blockchain/storage/storage.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/types" 7 | "github.com/hashicorp/go-hclog" 8 | ) 9 | 10 | // Storage is a generic blockchain storage 11 | type Storage interface { 12 | ReadCanonicalHash(n uint64) (types.Hash, bool) 13 | WriteCanonicalHash(n uint64, hash types.Hash) error 14 | 15 | ReadHeadHash() (types.Hash, bool) 16 | ReadHeadNumber() (uint64, bool) 17 | WriteHeadHash(h types.Hash) error 18 | WriteHeadNumber(uint64) error 19 | 20 | WriteForks(forks []types.Hash) error 21 | ReadForks() ([]types.Hash, error) 22 | 23 | WriteTotalDifficulty(hash types.Hash, diff *big.Int) error 24 | ReadTotalDifficulty(hash types.Hash) (*big.Int, bool) 25 | 26 | WriteHeader(h *types.Header) error 27 | ReadHeader(hash types.Hash) (*types.Header, error) 28 | 29 | WriteCanonicalHeader(h *types.Header, diff *big.Int) error 30 | 31 | WriteBody(hash types.Hash, body *types.Body) error 32 | ReadBody(hash types.Hash) (*types.Body, error) 33 | 34 | WriteReceipts(hash types.Hash, receipts []*types.Receipt) error 35 | ReadReceipts(hash types.Hash) ([]*types.Receipt, error) 36 | 37 | WriteTxLookup(hash types.Hash, blockHash types.Hash) error 38 | ReadTxLookup(hash types.Hash) (types.Hash, bool) 39 | 40 | Close() error 41 | } 42 | 43 | // Factory is a factory method to create a blockchain storage 44 | type Factory func(config map[string]interface{}, logger hclog.Logger) (Storage, error) 45 | -------------------------------------------------------------------------------- /blockchain/storage/utils.go: -------------------------------------------------------------------------------- 1 | package storage 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/types" 5 | "github.com/umbracle/fastrlp" 6 | ) 7 | 8 | type Forks []types.Hash 9 | 10 | // MarshalRLPTo is a wrapper function for calling the type marshal implementation 11 | func (f *Forks) MarshalRLPTo(dst []byte) []byte { 12 | return types.MarshalRLPTo(f.MarshalRLPWith, dst) 13 | } 14 | 15 | // MarshalRLPWith is the actual RLP marshal implementation for the type 16 | func (f *Forks) MarshalRLPWith(ar *fastrlp.Arena) *fastrlp.Value { 17 | var vr *fastrlp.Value 18 | 19 | if len(*f) == 0 { 20 | vr = ar.NewNullArray() 21 | } else { 22 | vr = ar.NewArray() 23 | 24 | for _, fork := range *f { 25 | vr.Set(ar.NewCopyBytes(fork[:])) 26 | } 27 | } 28 | 29 | return vr 30 | } 31 | 32 | // UnmarshalRLP is a wrapper function for calling the type unmarshal implementation 33 | func (f *Forks) UnmarshalRLP(input []byte) error { 34 | return types.UnmarshalRlp(f.UnmarshalRLPFrom, input) 35 | } 36 | 37 | // UnmarshalRLPFrom is the actual RLP unmarshal implementation for the type 38 | func (f *Forks) UnmarshalRLPFrom(p *fastrlp.Parser, v *fastrlp.Value) error { 39 | elems, err := v.GetElems() 40 | if err != nil { 41 | return err 42 | } 43 | 44 | forks := make([]types.Hash, len(elems)) 45 | for indx, elem := range elems { 46 | if err := elem.GetHash(forks[indx][:]); err != nil { 47 | return err 48 | } 49 | } 50 | 51 | *f = forks 52 | 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /blockchain/subscription_test.go: -------------------------------------------------------------------------------- 1 | package blockchain 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | "time" 7 | 8 | "github.com/ExzoNetwork/ExzoCoin/types" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestSubscription(t *testing.T) { 13 | t.Parallel() 14 | 15 | var ( 16 | e = &eventStream{} 17 | sub = e.subscribe() 18 | caughtEventNum = uint64(0) 19 | event = &Event{ 20 | NewChain: []*types.Header{ 21 | { 22 | Number: 100, 23 | }, 24 | }, 25 | } 26 | 27 | wg sync.WaitGroup 28 | ) 29 | 30 | defer sub.Close() 31 | 32 | updateCh := sub.GetEventCh() 33 | 34 | wg.Add(1) 35 | 36 | go func() { 37 | defer wg.Done() 38 | 39 | select { 40 | case ev := <-updateCh: 41 | caughtEventNum = ev.NewChain[0].Number 42 | case <-time.After(5 * time.Second): 43 | } 44 | }() 45 | 46 | // Send the event to the channel 47 | e.push(event) 48 | 49 | // Wait for the event to be parsed 50 | wg.Wait() 51 | 52 | assert.Equal(t, event.NewChain[0].Number, caughtEventNum) 53 | } 54 | -------------------------------------------------------------------------------- /chain/params_test.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestParamsForks(t *testing.T) { 10 | cases := []struct { 11 | input string 12 | output *Forks 13 | }{ 14 | { 15 | input: `{ 16 | "homestead": 1000 17 | }`, 18 | output: &Forks{ 19 | Homestead: NewFork(1000), 20 | }, 21 | }, 22 | } 23 | 24 | for _, c := range cases { 25 | var dec *Forks 26 | if err := json.Unmarshal([]byte(c.input), &dec); err != nil { 27 | if c.output != nil { 28 | t.Fatal(err) 29 | } 30 | } else if !reflect.DeepEqual(dec, c.output) { 31 | t.Fatal("bad") 32 | } 33 | } 34 | } 35 | 36 | func TestParamsForksInTime(t *testing.T) { 37 | f := Forks{ 38 | Homestead: NewFork(0), 39 | Byzantium: NewFork(1000), 40 | Constantinople: NewFork(1001), 41 | EIP150: NewFork(2000), 42 | } 43 | 44 | ff := f.At(1000) 45 | 46 | expect := func(name string, found bool, expect bool) { 47 | if expect != found { 48 | t.Fatalf("fork %s should be %v but found %v", name, expect, found) 49 | } 50 | } 51 | 52 | expect("homestead", ff.Homestead, true) 53 | expect("byzantium", ff.Byzantium, true) 54 | expect("constantinople", ff.Constantinople, false) 55 | expect("eip150", ff.EIP150, false) 56 | } 57 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: auto 6 | threshold: 0% -------------------------------------------------------------------------------- /command/backup/backup.go: -------------------------------------------------------------------------------- 1 | package backup 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | func GetCommand() *cobra.Command { 11 | backupCmd := &cobra.Command{ 12 | Use: "backup", 13 | Short: "Create blockchain backup file by fetching blockchain data from the running node", 14 | PreRunE: runPreRun, 15 | Run: runCommand, 16 | } 17 | 18 | helper.RegisterGRPCAddressFlag(backupCmd) 19 | 20 | setFlags(backupCmd) 21 | helper.SetRequiredFlags(backupCmd, params.getRequiredFlags()) 22 | 23 | return backupCmd 24 | } 25 | 26 | func setFlags(cmd *cobra.Command) { 27 | cmd.Flags().StringVar( 28 | ¶ms.out, 29 | outFlag, 30 | "", 31 | "the export path for the backup", 32 | ) 33 | 34 | cmd.Flags().StringVar( 35 | ¶ms.fromRaw, 36 | fromFlag, 37 | "0", 38 | "the beginning height of the chain in backup", 39 | ) 40 | 41 | cmd.Flags().StringVar( 42 | ¶ms.toRaw, 43 | toFlag, 44 | "", 45 | "the end height of the chain in backup", 46 | ) 47 | } 48 | 49 | func runPreRun(_ *cobra.Command, _ []string) error { 50 | return params.validateFlags() 51 | } 52 | 53 | func runCommand(cmd *cobra.Command, _ []string) { 54 | outputter := command.InitializeOutputter(cmd) 55 | defer outputter.WriteOutput() 56 | 57 | if err := params.createBackup(helper.GetGRPCAddress(cmd)); err != nil { 58 | outputter.SetError(err) 59 | 60 | return 61 | } 62 | 63 | outputter.SetCommandResult(params.getResult()) 64 | } 65 | -------------------------------------------------------------------------------- /command/backup/result.go: -------------------------------------------------------------------------------- 1 | package backup 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | type BackupResult struct { 11 | From uint64 `json:"from"` 12 | To uint64 `json:"to"` 13 | Out string `json:"out"` 14 | } 15 | 16 | func (r *BackupResult) GetOutput() string { 17 | var buffer bytes.Buffer 18 | 19 | buffer.WriteString("\n[BACKUP]\n") 20 | buffer.WriteString("Exported backup file successfully:\n") 21 | buffer.WriteString(helper.FormatKV([]string{ 22 | fmt.Sprintf("File|%s", r.Out), 23 | fmt.Sprintf("From|%d", r.From), 24 | fmt.Sprintf("To|%d", r.To), 25 | })) 26 | 27 | return buffer.String() 28 | } 29 | -------------------------------------------------------------------------------- /command/cli_output.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | type CLIOutput struct { 9 | commonOutputFormatter 10 | } 11 | 12 | func newCLIOutput() *CLIOutput { 13 | return &CLIOutput{} 14 | } 15 | 16 | func (cli *CLIOutput) WriteOutput() { 17 | if cli.errorOutput != nil { 18 | _, _ = fmt.Fprintln(os.Stderr, cli.getErrorOutput()) 19 | 20 | return 21 | } 22 | 23 | _, _ = fmt.Fprintln(os.Stdout, cli.getCommandOutput()) 24 | } 25 | 26 | func (cli *CLIOutput) getErrorOutput() string { 27 | return cli.errorOutput.Error() 28 | } 29 | 30 | func (cli *CLIOutput) getCommandOutput() string { 31 | return cli.commandOutput.GetOutput() 32 | } 33 | -------------------------------------------------------------------------------- /command/common_output.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | type commonOutputFormatter struct { 4 | errorOutput error 5 | commandOutput CommandResult 6 | } 7 | 8 | func (c *commonOutputFormatter) SetError(err error) { 9 | c.errorOutput = err 10 | } 11 | 12 | func (c *commonOutputFormatter) SetCommandResult(result CommandResult) { 13 | c.commandOutput = result 14 | } 15 | -------------------------------------------------------------------------------- /command/default.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import "github.com/ExzoNetwork/ExzoCoin/server" 4 | 5 | const ( 6 | DefaultGenesisFileName = "genesis.json" 7 | DefaultChainName = "exzocoin" 8 | DefaultChainID = 1229 9 | DefaultPremineBalance = "0xD3C21BCECCEDA1000000" // 1 million units of native network currency 10 | DefaultConsensus = server.IBFTConsensus 11 | DefaultGenesisGasUsed = 458752 // 0x70000 12 | DefaultGenesisGasLimit = 5242880 // 0x500000 13 | ) 14 | 15 | const ( 16 | JSONOutputFlag = "json" 17 | GRPCAddressFlag = "grpc-address" 18 | JSONRPCFlag = "jsonrpc" 19 | ) 20 | 21 | // GRPCAddressFlagLEGACY Legacy flag that needs to be present to preserve backwards 22 | // compatibility with running clients 23 | const ( 24 | GRPCAddressFlagLEGACY = "grpc" 25 | ) 26 | -------------------------------------------------------------------------------- /command/genesis/predeploy/genesis_predeploy.go: -------------------------------------------------------------------------------- 1 | package predeploy 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func GetCommand() *cobra.Command { 12 | genesisPredeployCmd := &cobra.Command{ 13 | Use: "predeploy", 14 | Short: "Specifies the contract to be predeployed on chain start", 15 | PreRunE: runPreRun, 16 | Run: runCommand, 17 | } 18 | 19 | setFlags(genesisPredeployCmd) 20 | helper.SetRequiredFlags(genesisPredeployCmd, params.getRequiredFlags()) 21 | 22 | return genesisPredeployCmd 23 | } 24 | 25 | func setFlags(cmd *cobra.Command) { 26 | cmd.Flags().StringVar( 27 | ¶ms.genesisPath, 28 | chainFlag, 29 | fmt.Sprintf("./%s", command.DefaultGenesisFileName), 30 | "the genesis file to update", 31 | ) 32 | 33 | cmd.Flags().StringVar( 34 | ¶ms.addressRaw, 35 | predeployAddressFlag, 36 | predeployAddressMin.String(), 37 | fmt.Sprintf("the address to predeploy to. Must be >= %s", predeployAddressMin.String()), 38 | ) 39 | 40 | cmd.Flags().StringVar( 41 | ¶ms.artifactsPath, 42 | artifactsPathFlag, 43 | "", 44 | "the path to the contract artifacts JSON", 45 | ) 46 | 47 | cmd.Flags().StringArrayVar( 48 | ¶ms.constructorArgs, 49 | constructorArgsPath, 50 | []string{}, 51 | "the constructor arguments, if any", 52 | ) 53 | } 54 | 55 | func runPreRun(_ *cobra.Command, _ []string) error { 56 | return params.initRawParams() 57 | } 58 | 59 | func runCommand(cmd *cobra.Command, _ []string) { 60 | outputter := command.InitializeOutputter(cmd) 61 | defer outputter.WriteOutput() 62 | 63 | if err := params.updateGenesisConfig(); err != nil { 64 | outputter.SetError(err) 65 | 66 | return 67 | } 68 | 69 | if err := params.overrideGenesisConfig(); err != nil { 70 | outputter.SetError(err) 71 | 72 | return 73 | } 74 | 75 | outputter.SetCommandResult(params.getResult()) 76 | } 77 | -------------------------------------------------------------------------------- /command/genesis/predeploy/result.go: -------------------------------------------------------------------------------- 1 | package predeploy 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | type GenesisPredeployResult struct { 11 | Address string `json:"address"` 12 | } 13 | 14 | func (r *GenesisPredeployResult) GetOutput() string { 15 | var buffer bytes.Buffer 16 | 17 | buffer.WriteString("\n[SMART CONTRACT PREDEPLOYMENT]\n") 18 | 19 | outputs := []string{ 20 | fmt.Sprintf("Address|%s", r.Address), 21 | } 22 | 23 | buffer.WriteString(helper.FormatKV(outputs)) 24 | buffer.WriteString("\n") 25 | 26 | return buffer.String() 27 | } 28 | -------------------------------------------------------------------------------- /command/genesis/result.go: -------------------------------------------------------------------------------- 1 | package genesis 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type GenesisResult struct { 8 | Message string `json:"message"` 9 | } 10 | 11 | func (r *GenesisResult) GetOutput() string { 12 | var buffer bytes.Buffer 13 | 14 | buffer.WriteString("\n[GENESIS SUCCESS]\n") 15 | buffer.WriteString(r.Message) 16 | 17 | return buffer.String() 18 | } 19 | -------------------------------------------------------------------------------- /command/ibft/candidates/ibft_candidates.go: -------------------------------------------------------------------------------- 1 | package candidates 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ibftOp "github.com/ExzoNetwork/ExzoCoin/consensus/ibft/proto" 9 | "github.com/spf13/cobra" 10 | empty "google.golang.org/protobuf/types/known/emptypb" 11 | ) 12 | 13 | func GetCommand() *cobra.Command { 14 | return &cobra.Command{ 15 | Use: "candidates", 16 | Short: "Queries the current set of proposed candidates, as well as candidates that have not been included yet", 17 | Run: runCommand, 18 | } 19 | } 20 | 21 | func runCommand(cmd *cobra.Command, _ []string) { 22 | outputter := command.InitializeOutputter(cmd) 23 | defer outputter.WriteOutput() 24 | 25 | candidatesResponse, err := getIBFTCandidates(helper.GetGRPCAddress(cmd)) 26 | if err != nil { 27 | outputter.SetError(err) 28 | 29 | return 30 | } 31 | 32 | outputter.SetCommandResult( 33 | newIBFTCandidatesResult(candidatesResponse), 34 | ) 35 | } 36 | 37 | func getIBFTCandidates(grpcAddress string) (*ibftOp.CandidatesResp, error) { 38 | client, err := helper.GetIBFTOperatorClientConnection( 39 | grpcAddress, 40 | ) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | return client.Candidates(context.Background(), &empty.Empty{}) 46 | } 47 | -------------------------------------------------------------------------------- /command/ibft/candidates/result.go: -------------------------------------------------------------------------------- 1 | package candidates 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ibftHelper "github.com/ExzoNetwork/ExzoCoin/command/ibft/helper" 9 | ibftOp "github.com/ExzoNetwork/ExzoCoin/consensus/ibft/proto" 10 | ) 11 | 12 | type IBFTCandidate struct { 13 | Address string `json:"address"` 14 | Vote ibftHelper.Vote `json:"vote"` 15 | } 16 | 17 | type IBFTCandidatesResult struct { 18 | Candidates []IBFTCandidate `json:"candidates"` 19 | } 20 | 21 | func newIBFTCandidatesResult(resp *ibftOp.CandidatesResp) *IBFTCandidatesResult { 22 | res := &IBFTCandidatesResult{ 23 | Candidates: make([]IBFTCandidate, len(resp.Candidates)), 24 | } 25 | 26 | for i, c := range resp.Candidates { 27 | res.Candidates[i].Address = c.Address 28 | res.Candidates[i].Vote = ibftHelper.BoolToVote(c.Auth) 29 | } 30 | 31 | return res 32 | } 33 | 34 | func (r *IBFTCandidatesResult) GetOutput() string { 35 | var buffer bytes.Buffer 36 | 37 | buffer.WriteString("\n[IBFT CANDIDATES]\n") 38 | 39 | if num := len(r.Candidates); num == 0 { 40 | buffer.WriteString("No candidates found") 41 | } else { 42 | buffer.WriteString(fmt.Sprintf("Number of candidates: %d\n\n", num)) 43 | buffer.WriteString(formatCandidates(r.Candidates)) 44 | } 45 | 46 | buffer.WriteString("\n") 47 | 48 | return buffer.String() 49 | } 50 | 51 | func formatCandidates(candidates []IBFTCandidate) string { 52 | generatedCandidates := make([]string, 0, len(candidates)+1) 53 | 54 | generatedCandidates = append(generatedCandidates, "Address|Vote") 55 | for _, c := range candidates { 56 | generatedCandidates = append(generatedCandidates, fmt.Sprintf("%s|%s", c.Address, c.Vote)) 57 | } 58 | 59 | return helper.FormatKV(generatedCandidates) 60 | } 61 | -------------------------------------------------------------------------------- /command/ibft/helper/vote.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | type Vote string 4 | 5 | const ( 6 | VoteAdd = "ADD" 7 | VoteRemove = "REMOVE" 8 | ) 9 | 10 | func BoolToVote(vote bool) Vote { 11 | if vote { 12 | return VoteAdd 13 | } 14 | 15 | return VoteRemove 16 | } 17 | 18 | func VoteToString(vote Vote) string { 19 | return string(vote) 20 | } 21 | -------------------------------------------------------------------------------- /command/ibft/ibft.go: -------------------------------------------------------------------------------- 1 | package ibft 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 5 | "github.com/ExzoNetwork/ExzoCoin/command/ibft/candidates" 6 | "github.com/ExzoNetwork/ExzoCoin/command/ibft/propose" 7 | "github.com/ExzoNetwork/ExzoCoin/command/ibft/quorum" 8 | "github.com/ExzoNetwork/ExzoCoin/command/ibft/snapshot" 9 | "github.com/ExzoNetwork/ExzoCoin/command/ibft/status" 10 | _switch "github.com/ExzoNetwork/ExzoCoin/command/ibft/switch" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | func GetCommand() *cobra.Command { 15 | ibftCmd := &cobra.Command{ 16 | Use: "ibft", 17 | Short: "Top level IBFT command for interacting with the IBFT consensus. Only accepts subcommands.", 18 | } 19 | 20 | helper.RegisterGRPCAddressFlag(ibftCmd) 21 | 22 | registerSubcommands(ibftCmd) 23 | 24 | return ibftCmd 25 | } 26 | 27 | func registerSubcommands(baseCmd *cobra.Command) { 28 | baseCmd.AddCommand( 29 | // ibft status 30 | status.GetCommand(), 31 | // ibft snapshot 32 | snapshot.GetCommand(), 33 | // ibft propose 34 | propose.GetCommand(), 35 | // ibft candidates 36 | candidates.GetCommand(), 37 | // ibft switch 38 | _switch.GetCommand(), 39 | // ibft quorum 40 | quorum.GetCommand(), 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /command/ibft/propose/ibft_propose.go: -------------------------------------------------------------------------------- 1 | package propose 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 10 | ) 11 | 12 | func GetCommand() *cobra.Command { 13 | ibftSnapshotCmd := &cobra.Command{ 14 | Use: "propose", 15 | Short: "Proposes a new candidate to be added or removed from the validator set", 16 | PreRunE: runPreRun, 17 | Run: runCommand, 18 | } 19 | 20 | setFlags(ibftSnapshotCmd) 21 | 22 | helper.SetRequiredFlags(ibftSnapshotCmd, params.getRequiredFlags()) 23 | 24 | return ibftSnapshotCmd 25 | } 26 | 27 | func setFlags(cmd *cobra.Command) { 28 | cmd.Flags().StringVar( 29 | ¶ms.addressRaw, 30 | addressFlag, 31 | "", 32 | "the address of the account to be voted for", 33 | ) 34 | 35 | cmd.Flags().StringVar( 36 | ¶ms.rawBLSPublicKey, 37 | blsFlag, 38 | "", 39 | "the BLS Public Key of the account to be voted for", 40 | ) 41 | 42 | cmd.Flags().StringVar( 43 | ¶ms.vote, 44 | voteFlag, 45 | "", 46 | fmt.Sprintf( 47 | "requested change to the validator set. Possible values: [%s, %s]", 48 | authVote, 49 | dropVote, 50 | ), 51 | ) 52 | 53 | cmd.MarkFlagsRequiredTogether(addressFlag, voteFlag) 54 | } 55 | 56 | func runPreRun(_ *cobra.Command, _ []string) error { 57 | if err := params.validateFlags(); err != nil { 58 | return err 59 | } 60 | 61 | return params.initRawParams() 62 | } 63 | 64 | func runCommand(cmd *cobra.Command, _ []string) { 65 | outputter := command.InitializeOutputter(cmd) 66 | defer outputter.WriteOutput() 67 | 68 | if err := params.proposeCandidate(helper.GetGRPCAddress(cmd)); err != nil { 69 | outputter.SetError(err) 70 | 71 | return 72 | } 73 | 74 | outputter.SetCommandResult(params.getResult()) 75 | } 76 | -------------------------------------------------------------------------------- /command/ibft/propose/result.go: -------------------------------------------------------------------------------- 1 | package propose 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | type IBFTProposeResult struct { 9 | Address string `json:"-"` 10 | Vote string `json:"-"` 11 | } 12 | 13 | func (r *IBFTProposeResult) GetOutput() string { 14 | var buffer bytes.Buffer 15 | 16 | buffer.WriteString("\n[IBFT PROPOSE]\n") 17 | buffer.WriteString(r.Message()) 18 | buffer.WriteString("\n") 19 | 20 | return buffer.String() 21 | } 22 | 23 | func (r *IBFTProposeResult) Message() string { 24 | if r.Vote == authVote { 25 | return fmt.Sprintf( 26 | "Successfully voted for the addition of address [%s] to the validator set", 27 | r.Address, 28 | ) 29 | } 30 | 31 | return fmt.Sprintf( 32 | "Successfully voted for the removal of validator at address [%s] from the validator set", 33 | r.Address, 34 | ) 35 | } 36 | 37 | func (r *IBFTProposeResult) MarshalJSON() ([]byte, error) { 38 | return []byte(fmt.Sprintf(`{"message": "%s"}`, r.Message())), nil 39 | } 40 | -------------------------------------------------------------------------------- /command/ibft/quorum/ibft_quorum.go: -------------------------------------------------------------------------------- 1 | package quorum 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func GetCommand() *cobra.Command { 12 | ibftQuorumCmd := &cobra.Command{ 13 | Use: "quorum", 14 | Short: "Specify the block number after which quorum optimal will be used for reaching consensus", 15 | PreRunE: runPreRun, 16 | Run: runCommand, 17 | } 18 | 19 | setFlags(ibftQuorumCmd) 20 | helper.SetRequiredFlags(ibftQuorumCmd, params.getRequiredFlags()) 21 | 22 | return ibftQuorumCmd 23 | } 24 | 25 | func setFlags(cmd *cobra.Command) { 26 | cmd.Flags().StringVar( 27 | ¶ms.genesisPath, 28 | chainFlag, 29 | fmt.Sprintf("./%s", command.DefaultGenesisFileName), 30 | "the genesis file to update", 31 | ) 32 | 33 | cmd.Flags().Uint64Var( 34 | ¶ms.from, 35 | fromFlag, 36 | 0, 37 | "the height to switch the quorum calculation", 38 | ) 39 | } 40 | 41 | func runPreRun(_ *cobra.Command, _ []string) error { 42 | return params.initRawParams() 43 | } 44 | 45 | func runCommand(cmd *cobra.Command, _ []string) { 46 | outputter := command.InitializeOutputter(cmd) 47 | defer outputter.WriteOutput() 48 | 49 | if err := params.updateGenesisConfig(); err != nil { 50 | outputter.SetError(err) 51 | 52 | return 53 | } 54 | 55 | if err := params.overrideGenesisConfig(); err != nil { 56 | outputter.SetError(err) 57 | 58 | return 59 | } 60 | 61 | outputter.SetCommandResult(params.getResult()) 62 | } 63 | -------------------------------------------------------------------------------- /command/ibft/quorum/result.go: -------------------------------------------------------------------------------- 1 | package quorum 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | "github.com/ExzoNetwork/ExzoCoin/helper/common" 9 | ) 10 | 11 | type IBFTQuorumResult struct { 12 | Chain string `json:"chain"` 13 | From common.JSONNumber `json:"from"` 14 | } 15 | 16 | func (r *IBFTQuorumResult) GetOutput() string { 17 | var buffer bytes.Buffer 18 | 19 | buffer.WriteString("\n[NEW IBFT QUORUM START]\n") 20 | 21 | outputs := []string{ 22 | fmt.Sprintf("Chain|%s", r.Chain), 23 | fmt.Sprintf("From|%d", r.From.Value), 24 | } 25 | 26 | buffer.WriteString(helper.FormatKV(outputs)) 27 | buffer.WriteString("\n") 28 | 29 | return buffer.String() 30 | } 31 | -------------------------------------------------------------------------------- /command/ibft/snapshot/ibft_snapshot.go: -------------------------------------------------------------------------------- 1 | package snapshot 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command" 5 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func GetCommand() *cobra.Command { 10 | ibftSnapshotCmd := &cobra.Command{ 11 | Use: "snapshot", 12 | Short: "Returns the IBFT snapshot at the latest block number, unless a block number is specified", 13 | Run: runCommand, 14 | } 15 | 16 | setFlags(ibftSnapshotCmd) 17 | 18 | return ibftSnapshotCmd 19 | } 20 | 21 | func setFlags(cmd *cobra.Command) { 22 | cmd.Flags().IntVar( 23 | ¶ms.blockNumber, 24 | numberFlag, 25 | -1, 26 | "the block height (number) for the snapshot", 27 | ) 28 | } 29 | 30 | func runCommand(cmd *cobra.Command, _ []string) { 31 | outputter := command.InitializeOutputter(cmd) 32 | defer outputter.WriteOutput() 33 | 34 | if err := params.initSnapshot(helper.GetGRPCAddress(cmd)); err != nil { 35 | outputter.SetError(err) 36 | 37 | return 38 | } 39 | 40 | result, err := newIBFTSnapshotResult(params.snapshot) 41 | if err != nil { 42 | outputter.SetError(err) 43 | 44 | return 45 | } 46 | 47 | outputter.SetCommandResult(result) 48 | } 49 | -------------------------------------------------------------------------------- /command/ibft/snapshot/params.go: -------------------------------------------------------------------------------- 1 | package snapshot 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 7 | ibftOp "github.com/ExzoNetwork/ExzoCoin/consensus/ibft/proto" 8 | ) 9 | 10 | const ( 11 | numberFlag = "number" 12 | ) 13 | 14 | var ( 15 | params = &snapshotParams{} 16 | ) 17 | 18 | type snapshotParams struct { 19 | blockNumber int 20 | 21 | snapshot *ibftOp.Snapshot 22 | } 23 | 24 | func (p *snapshotParams) initSnapshot(grpcAddress string) error { 25 | ibftClient, err := helper.GetIBFTOperatorClientConnection(grpcAddress) 26 | if err != nil { 27 | return err 28 | } 29 | 30 | snapshot, err := ibftClient.GetSnapshot( 31 | context.Background(), 32 | p.getSnapshotRequest(), 33 | ) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | p.snapshot = snapshot 39 | 40 | return nil 41 | } 42 | 43 | func (p *snapshotParams) getSnapshotRequest() *ibftOp.SnapshotReq { 44 | req := &ibftOp.SnapshotReq{ 45 | Latest: true, 46 | } 47 | 48 | if p.blockNumber >= 0 { 49 | req.Latest = false 50 | req.Number = uint64(p.blockNumber) 51 | } 52 | 53 | return req 54 | } 55 | -------------------------------------------------------------------------------- /command/ibft/status/ibft_status.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ibftOp "github.com/ExzoNetwork/ExzoCoin/consensus/ibft/proto" 9 | "github.com/spf13/cobra" 10 | empty "google.golang.org/protobuf/types/known/emptypb" 11 | ) 12 | 13 | func GetCommand() *cobra.Command { 14 | return &cobra.Command{ 15 | Use: "status", 16 | Short: "Returns the current validator key of the IBFT client", 17 | Run: runCommand, 18 | } 19 | } 20 | 21 | func runCommand(cmd *cobra.Command, _ []string) { 22 | outputter := command.InitializeOutputter(cmd) 23 | defer outputter.WriteOutput() 24 | 25 | statusResponse, err := getIBFTStatus(helper.GetGRPCAddress(cmd)) 26 | if err != nil { 27 | outputter.SetError(err) 28 | 29 | return 30 | } 31 | 32 | outputter.SetCommandResult(&IBFTStatusResult{ 33 | ValidatorKey: statusResponse.Key, 34 | }) 35 | } 36 | 37 | func getIBFTStatus(grpcAddress string) (*ibftOp.IbftStatusResp, error) { 38 | client, err := helper.GetIBFTOperatorClientConnection( 39 | grpcAddress, 40 | ) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | return client.Status(context.Background(), &empty.Empty{}) 46 | } 47 | -------------------------------------------------------------------------------- /command/ibft/status/result.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | type IBFTStatusResult struct { 11 | ValidatorKey string `json:"validator_key"` 12 | } 13 | 14 | func (r *IBFTStatusResult) GetOutput() string { 15 | var buffer bytes.Buffer 16 | 17 | buffer.WriteString("\n[VALIDATOR STATUS]\n") 18 | buffer.WriteString(helper.FormatKV([]string{ 19 | fmt.Sprintf("Validator key|%s", r.ValidatorKey), 20 | })) 21 | buffer.WriteString("\n") 22 | 23 | return buffer.String() 24 | } 25 | -------------------------------------------------------------------------------- /command/ibft/switch/result.go: -------------------------------------------------------------------------------- 1 | package ibftswitch 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | "github.com/ExzoNetwork/ExzoCoin/consensus/ibft/fork" 9 | "github.com/ExzoNetwork/ExzoCoin/helper/common" 10 | "github.com/ExzoNetwork/ExzoCoin/validators" 11 | ) 12 | 13 | type IBFTSwitchResult struct { 14 | Chain string `json:"chain"` 15 | Type fork.IBFTType `json:"type"` 16 | ValidatorType validators.ValidatorType `json:"validator_type"` 17 | From common.JSONNumber `json:"from"` 18 | Deployment *common.JSONNumber `json:"deployment,omitempty"` 19 | MaxValidatorCount common.JSONNumber `json:"maxValidatorCount"` 20 | MinValidatorCount common.JSONNumber `json:"minValidatorCount"` 21 | } 22 | 23 | func (r *IBFTSwitchResult) GetOutput() string { 24 | var buffer bytes.Buffer 25 | 26 | buffer.WriteString("\n[NEW IBFT FORK]\n") 27 | 28 | outputs := []string{ 29 | fmt.Sprintf("Chain|%s", r.Chain), 30 | fmt.Sprintf("Type|%s", r.Type), 31 | fmt.Sprintf("ValidatorType|%s", r.ValidatorType), 32 | } 33 | 34 | if r.Deployment != nil { 35 | outputs = append(outputs, fmt.Sprintf("Deployment|%d", r.Deployment.Value)) 36 | } 37 | 38 | outputs = append(outputs, fmt.Sprintf("From|%d", r.From.Value)) 39 | 40 | if r.Type == fork.PoS { 41 | outputs = append(outputs, 42 | fmt.Sprintf("MaxValidatorCount|%d", r.MaxValidatorCount.Value), 43 | fmt.Sprintf("MinValidatorCount|%d", r.MinValidatorCount.Value), 44 | ) 45 | } 46 | 47 | buffer.WriteString(helper.FormatKV(outputs)) 48 | buffer.WriteString("\n") 49 | 50 | return buffer.String() 51 | } 52 | -------------------------------------------------------------------------------- /command/json_output.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type JSONOutput struct { 10 | commonOutputFormatter 11 | } 12 | 13 | func (jo *JSONOutput) WriteOutput() { 14 | if jo.errorOutput != nil { 15 | _, _ = fmt.Fprintln(os.Stderr, jo.getErrorOutput()) 16 | 17 | return 18 | } 19 | 20 | _, _ = fmt.Fprintln(os.Stdout, jo.getCommandOutput()) 21 | } 22 | 23 | func newJSONOutput() *JSONOutput { 24 | return &JSONOutput{} 25 | } 26 | 27 | func (jo *JSONOutput) getErrorOutput() string { 28 | return marshalJSONToString( 29 | struct { 30 | Err string `json:"error"` 31 | }{ 32 | Err: jo.errorOutput.Error(), 33 | }, 34 | ) 35 | } 36 | 37 | func (jo *JSONOutput) getCommandOutput() string { 38 | return marshalJSONToString(jo.commandOutput) 39 | } 40 | 41 | func marshalJSONToString(input interface{}) string { 42 | bytes, err := json.Marshal(input) 43 | if err != nil { 44 | return err.Error() 45 | } 46 | 47 | return string(bytes) 48 | } 49 | -------------------------------------------------------------------------------- /command/license/license.go: -------------------------------------------------------------------------------- 1 | package license 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/licenses" 8 | ) 9 | 10 | func GetCommand() *cobra.Command { 11 | return &cobra.Command{ 12 | Use: "license", 13 | Short: "Returns Polygon Edge license and dependency attributions", 14 | Args: cobra.NoArgs, 15 | Run: runCommand, 16 | } 17 | } 18 | 19 | func runCommand(cmd *cobra.Command, _ []string) { 20 | outputter := command.InitializeOutputter(cmd) 21 | defer outputter.WriteOutput() 22 | 23 | bsdLicenses, err := licenses.GetBSDLicenses() 24 | if err != nil { 25 | outputter.SetError(err) 26 | 27 | return 28 | } 29 | 30 | outputter.SetCommandResult( 31 | &LicenseResult{ 32 | BSDLicenses: bsdLicenses, 33 | }, 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /command/license/result.go: -------------------------------------------------------------------------------- 1 | package license 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/licenses" 8 | ) 9 | 10 | type LicenseResult struct { 11 | BSDLicenses []licenses.DepLicense `json:"bsd_licenses"` 12 | } 13 | 14 | func (r *LicenseResult) GetOutput() string { 15 | var buffer bytes.Buffer 16 | 17 | buffer.WriteString("\n[LICENSE]\n\n") 18 | buffer.WriteString(licenses.License) 19 | 20 | buffer.WriteString("\n[DEPENDENCY ATTRIBUTIONS]\n\n") 21 | 22 | for idx, l := range r.BSDLicenses { 23 | // put a blank line between attributions 24 | if idx != 0 { 25 | buffer.WriteString("\n") 26 | } 27 | 28 | name := l.Name 29 | if l.Version != nil { 30 | name += " " + *l.Version 31 | } 32 | 33 | buffer.WriteString(fmt.Sprintf( 34 | " This product bundles %s,\n"+ 35 | " which is available under a \"%s\" license.\n"+ 36 | " For details, see %s.\n", 37 | name, 38 | l.Type, 39 | l.Path, 40 | )) 41 | } 42 | 43 | return buffer.String() 44 | } 45 | -------------------------------------------------------------------------------- /command/monitor/result.go: -------------------------------------------------------------------------------- 1 | package monitor 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | "github.com/ExzoNetwork/ExzoCoin/server/proto" 9 | ) 10 | 11 | const ( 12 | eventAdded = "ADD BLOCK" 13 | eventRemoved = "REMOVE BLOCK" 14 | ) 15 | 16 | type BlockchainEvent struct { 17 | Type string `json:"type"` 18 | Number int64 `json:"number"` 19 | Hash string `json:"hash"` 20 | } 21 | 22 | type BlockChainEvents struct { 23 | Added []BlockchainEvent `json:"added"` 24 | Removed []BlockchainEvent `json:"removed"` 25 | } 26 | 27 | type BlockEventResult struct { 28 | Events BlockChainEvents `json:"events"` 29 | } 30 | 31 | func NewBlockEventResult(e *proto.BlockchainEvent) *BlockEventResult { 32 | res := &BlockEventResult{ 33 | Events: BlockChainEvents{ 34 | Added: make([]BlockchainEvent, len(e.Added)), 35 | Removed: make([]BlockchainEvent, len(e.Removed)), 36 | }, 37 | } 38 | 39 | for i, add := range e.Added { 40 | res.Events.Added[i].Type = eventAdded 41 | res.Events.Added[i].Number = add.Number 42 | res.Events.Added[i].Hash = add.Hash 43 | } 44 | 45 | for i, rem := range e.Removed { 46 | res.Events.Removed[i].Type = eventRemoved 47 | res.Events.Removed[i].Number = rem.Number 48 | res.Events.Removed[i].Hash = rem.Hash 49 | } 50 | 51 | return res 52 | } 53 | 54 | func (r *BlockEventResult) GetOutput() string { 55 | var buffer bytes.Buffer 56 | 57 | buffer.WriteString("\n[BLOCK EVENT]\n") 58 | 59 | for _, event := range r.getCombinedEvents() { 60 | buffer.WriteString(helper.FormatKV([]string{ 61 | fmt.Sprintf("Event Type|%s", event.Type), 62 | fmt.Sprintf("Block Number|%d", event.Number), 63 | fmt.Sprintf("Block Hash|%s", event.Hash), 64 | })) 65 | } 66 | 67 | return buffer.String() 68 | } 69 | 70 | func (r *BlockEventResult) getCombinedEvents() []BlockchainEvent { 71 | events := append([]BlockchainEvent{}, r.Events.Added...) 72 | 73 | return append(events, r.Events.Removed...) 74 | } 75 | -------------------------------------------------------------------------------- /command/output.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // OutputFormatter is the standardized interface all output formatters 8 | // should use 9 | type OutputFormatter interface { 10 | // getErrorOutput returns the CLI command error 11 | getErrorOutput() string 12 | 13 | // getCommandOutput returns the CLI command output 14 | getCommandOutput() string 15 | 16 | // SetError sets the encountered error 17 | SetError(err error) 18 | 19 | // SetCommandResult sets the result of the command execution 20 | SetCommandResult(result CommandResult) 21 | 22 | // WriteOutput writes the result / error output 23 | WriteOutput() 24 | } 25 | 26 | type CommandResult interface { 27 | GetOutput() string 28 | } 29 | 30 | func shouldOutputJSON(baseCmd *cobra.Command) bool { 31 | return baseCmd.Flag(JSONOutputFlag).Changed 32 | } 33 | 34 | func InitializeOutputter(cmd *cobra.Command) OutputFormatter { 35 | if shouldOutputJSON(cmd) { 36 | return newJSONOutput() 37 | } 38 | 39 | return newCLIOutput() 40 | } 41 | -------------------------------------------------------------------------------- /command/peers/add/params.go: -------------------------------------------------------------------------------- 1 | package add 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command" 8 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 9 | "github.com/ExzoNetwork/ExzoCoin/server/proto" 10 | ) 11 | 12 | var ( 13 | params = &addParams{ 14 | addedPeers: make([]string, 0), 15 | addErrors: make([]string, 0), 16 | } 17 | ) 18 | 19 | var ( 20 | errInvalidAddresses = errors.New("at least 1 peer address is required") 21 | ) 22 | 23 | const ( 24 | addrFlag = "addr" 25 | ) 26 | 27 | type addParams struct { 28 | peerAddresses []string 29 | 30 | systemClient proto.SystemClient 31 | 32 | addedPeers []string 33 | addErrors []string 34 | } 35 | 36 | func (p *addParams) getRequiredFlags() []string { 37 | return []string{ 38 | addrFlag, 39 | } 40 | } 41 | 42 | func (p *addParams) validateFlags() error { 43 | if len(p.peerAddresses) < 1 { 44 | return errInvalidAddresses 45 | } 46 | 47 | return nil 48 | } 49 | 50 | func (p *addParams) initSystemClient(grpcAddress string) error { 51 | systemClient, err := helper.GetSystemClientConnection(grpcAddress) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | p.systemClient = systemClient 57 | 58 | return nil 59 | } 60 | 61 | func (p *addParams) addPeers() { 62 | for _, address := range p.peerAddresses { 63 | if addErr := p.addPeer(address); addErr != nil { 64 | p.addErrors = append(p.addErrors, addErr.Error()) 65 | 66 | continue 67 | } 68 | 69 | p.addedPeers = append(p.addedPeers, address) 70 | } 71 | } 72 | 73 | func (p *addParams) addPeer(peerAddress string) error { 74 | if _, err := p.systemClient.PeersAdd( 75 | context.Background(), 76 | &proto.PeersAddRequest{ 77 | Id: peerAddress, 78 | }, 79 | ); err != nil { 80 | return err 81 | } 82 | 83 | return nil 84 | } 85 | 86 | func (p *addParams) getResult() command.CommandResult { 87 | return &PeersAddResult{ 88 | NumRequested: len(p.peerAddresses), 89 | NumAdded: len(p.addedPeers), 90 | Peers: p.addedPeers, 91 | Errors: p.addErrors, 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /command/peers/add/peers_add.go: -------------------------------------------------------------------------------- 1 | package add 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command" 5 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func GetCommand() *cobra.Command { 10 | peersAddCmd := &cobra.Command{ 11 | Use: "add", 12 | Short: "Adds new peers to the peer list, using the peer's libp2p address", 13 | PreRunE: runPreRun, 14 | Run: runCommand, 15 | } 16 | 17 | setFlags(peersAddCmd) 18 | helper.SetRequiredFlags(peersAddCmd, params.getRequiredFlags()) 19 | 20 | return peersAddCmd 21 | } 22 | 23 | func setFlags(cmd *cobra.Command) { 24 | cmd.Flags().StringArrayVar( 25 | ¶ms.peerAddresses, 26 | addrFlag, 27 | []string{}, 28 | "the libp2p addresses of the peers", 29 | ) 30 | } 31 | 32 | func runPreRun(_ *cobra.Command, _ []string) error { 33 | return params.validateFlags() 34 | } 35 | 36 | func runCommand(cmd *cobra.Command, _ []string) { 37 | outputter := command.InitializeOutputter(cmd) 38 | defer outputter.WriteOutput() 39 | 40 | if err := params.initSystemClient(helper.GetGRPCAddress(cmd)); err != nil { 41 | outputter.SetError(err) 42 | 43 | return 44 | } 45 | 46 | params.addPeers() 47 | 48 | outputter.SetCommandResult(params.getResult()) 49 | } 50 | -------------------------------------------------------------------------------- /command/peers/add/result.go: -------------------------------------------------------------------------------- 1 | package add 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | type PeersAddResult struct { 11 | NumRequested int `json:"num_requested"` 12 | NumAdded int `json:"num_added"` 13 | Peers []string `json:"peers"` 14 | Errors []string `json:"errors"` 15 | } 16 | 17 | func (r *PeersAddResult) GetOutput() string { 18 | var buffer bytes.Buffer 19 | 20 | buffer.WriteString("\n[PEERS ADDED]\n") 21 | buffer.WriteString(helper.FormatKV([]string{ 22 | fmt.Sprintf("Peers listed|%d", r.NumRequested), // The number of peers the user wanted to add 23 | fmt.Sprintf("Peers added|%d", r.NumAdded), // The number of peers that have been added 24 | })) 25 | 26 | if len(r.Peers) > 0 { 27 | buffer.WriteString("\n\n[LIST OF ADDED PEERS]\n") 28 | buffer.WriteString(helper.FormatList(r.Peers)) 29 | } 30 | 31 | if len(r.Errors) > 0 { 32 | buffer.WriteString("\n\n[ERRORS]\n") 33 | buffer.WriteString(helper.FormatList(r.Errors)) 34 | } 35 | 36 | buffer.WriteString("\n") 37 | 38 | return buffer.String() 39 | } 40 | -------------------------------------------------------------------------------- /command/peers/list/peers_list.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | "github.com/ExzoNetwork/ExzoCoin/server/proto" 9 | "github.com/spf13/cobra" 10 | empty "google.golang.org/protobuf/types/known/emptypb" 11 | ) 12 | 13 | func GetCommand() *cobra.Command { 14 | peersListCmd := &cobra.Command{ 15 | Use: "list", 16 | Short: "Returns the list of connected peers, including the current node", 17 | Run: runCommand, 18 | } 19 | 20 | return peersListCmd 21 | } 22 | 23 | func runCommand(cmd *cobra.Command, _ []string) { 24 | outputter := command.InitializeOutputter(cmd) 25 | defer outputter.WriteOutput() 26 | 27 | peersList, err := getPeersList(helper.GetGRPCAddress(cmd)) 28 | if err != nil { 29 | outputter.SetError(err) 30 | 31 | return 32 | } 33 | 34 | outputter.SetCommandResult( 35 | newPeersListResult(peersList.Peers), 36 | ) 37 | } 38 | 39 | func getPeersList(grpcAddress string) (*proto.PeersListResponse, error) { 40 | client, err := helper.GetSystemClientConnection(grpcAddress) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | return client.PeersList(context.Background(), &empty.Empty{}) 46 | } 47 | -------------------------------------------------------------------------------- /command/peers/list/result.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | "github.com/ExzoNetwork/ExzoCoin/server/proto" 9 | ) 10 | 11 | type PeersListResult struct { 12 | Peers []string `json:"peers"` 13 | } 14 | 15 | func newPeersListResult(peers []*proto.Peer) *PeersListResult { 16 | resultPeers := make([]string, len(peers)) 17 | for i, p := range peers { 18 | resultPeers[i] = p.Id 19 | } 20 | 21 | return &PeersListResult{ 22 | Peers: resultPeers, 23 | } 24 | } 25 | 26 | func (r *PeersListResult) GetOutput() string { 27 | var buffer bytes.Buffer 28 | 29 | buffer.WriteString("\n[PEERS LIST]\n") 30 | 31 | if len(r.Peers) == 0 { 32 | buffer.WriteString("No peers found") 33 | } else { 34 | buffer.WriteString(fmt.Sprintf("Number of peers: %d\n\n", len(r.Peers))) 35 | 36 | rows := make([]string, len(r.Peers)) 37 | for i, p := range r.Peers { 38 | rows[i] = fmt.Sprintf("[%d]|%s", i, p) 39 | } 40 | buffer.WriteString(helper.FormatKV(rows)) 41 | } 42 | 43 | buffer.WriteString("\n") 44 | 45 | return buffer.String() 46 | } 47 | -------------------------------------------------------------------------------- /command/peers/peers.go: -------------------------------------------------------------------------------- 1 | package peers 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 5 | "github.com/ExzoNetwork/ExzoCoin/command/peers/add" 6 | "github.com/ExzoNetwork/ExzoCoin/command/peers/list" 7 | "github.com/ExzoNetwork/ExzoCoin/command/peers/status" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func GetCommand() *cobra.Command { 12 | peersCmd := &cobra.Command{ 13 | Use: "peers", 14 | Short: "Top level command for interacting with the network peers. Only accepts subcommands.", 15 | } 16 | 17 | helper.RegisterGRPCAddressFlag(peersCmd) 18 | 19 | registerSubcommands(peersCmd) 20 | 21 | return peersCmd 22 | } 23 | 24 | func registerSubcommands(baseCmd *cobra.Command) { 25 | baseCmd.AddCommand( 26 | // peers status 27 | status.GetCommand(), 28 | // peers list 29 | list.GetCommand(), 30 | // peers add 31 | add.GetCommand(), 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /command/peers/status/params.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | "github.com/ExzoNetwork/ExzoCoin/server/proto" 9 | ) 10 | 11 | var ( 12 | params = &statusParams{} 13 | ) 14 | 15 | const ( 16 | peerIDFlag = "peer-id" 17 | ) 18 | 19 | type statusParams struct { 20 | peerID string 21 | 22 | peerStatus *proto.Peer 23 | } 24 | 25 | func (p *statusParams) getRequiredFlags() []string { 26 | return []string{ 27 | peerIDFlag, 28 | } 29 | } 30 | 31 | func (p *statusParams) initPeerInfo(grpcAddress string) error { 32 | systemClient, err := helper.GetSystemClientConnection(grpcAddress) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | peerStatus, err := systemClient.PeersStatus( 38 | context.Background(), 39 | &proto.PeersStatusRequest{ 40 | Id: p.peerID, 41 | }, 42 | ) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | p.peerStatus = peerStatus 48 | 49 | return nil 50 | } 51 | 52 | func (p *statusParams) getResult() command.CommandResult { 53 | return &PeersStatusResult{ 54 | ID: p.peerStatus.Id, 55 | Protocols: p.peerStatus.Protocols, 56 | Addresses: p.peerStatus.Addrs, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /command/peers/status/peers_status.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command" 5 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func GetCommand() *cobra.Command { 10 | peersStatusCmd := &cobra.Command{ 11 | Use: "status", 12 | Short: "Returns the status of the specified peer, using the libp2p ID of the peer node", 13 | Run: runCommand, 14 | } 15 | 16 | setFlags(peersStatusCmd) 17 | helper.SetRequiredFlags(peersStatusCmd, params.getRequiredFlags()) 18 | 19 | return peersStatusCmd 20 | } 21 | 22 | func setFlags(cmd *cobra.Command) { 23 | cmd.Flags().StringVar( 24 | ¶ms.peerID, 25 | peerIDFlag, 26 | "", 27 | "libp2p node ID of a specific peer within p2p network", 28 | ) 29 | } 30 | 31 | func runCommand(cmd *cobra.Command, _ []string) { 32 | outputter := command.InitializeOutputter(cmd) 33 | defer outputter.WriteOutput() 34 | 35 | if err := params.initPeerInfo(helper.GetGRPCAddress(cmd)); err != nil { 36 | outputter.SetError(err) 37 | 38 | return 39 | } 40 | 41 | outputter.SetCommandResult(params.getResult()) 42 | } 43 | -------------------------------------------------------------------------------- /command/peers/status/result.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | type PeersStatusResult struct { 11 | ID string `json:"id"` 12 | Protocols []string `json:"protocols"` 13 | Addresses []string `json:"addresses"` 14 | } 15 | 16 | func (r *PeersStatusResult) GetOutput() string { 17 | var buffer bytes.Buffer 18 | 19 | buffer.WriteString("\n[PEER STATUS]\n") 20 | buffer.WriteString(helper.FormatKV([]string{ 21 | fmt.Sprintf("ID|%s", r.ID), 22 | fmt.Sprintf("Protocols|%s", r.Protocols), 23 | fmt.Sprintf("Addresses|%s", r.Addresses), 24 | })) 25 | buffer.WriteString("\n") 26 | 27 | return buffer.String() 28 | } 29 | -------------------------------------------------------------------------------- /command/root/root.go: -------------------------------------------------------------------------------- 1 | package root 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/backup" 8 | "github.com/ExzoNetwork/ExzoCoin/command/genesis" 9 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 10 | "github.com/ExzoNetwork/ExzoCoin/command/ibft" 11 | "github.com/ExzoNetwork/ExzoCoin/command/license" 12 | "github.com/ExzoNetwork/ExzoCoin/command/monitor" 13 | "github.com/ExzoNetwork/ExzoCoin/command/peers" 14 | "github.com/ExzoNetwork/ExzoCoin/command/secrets" 15 | "github.com/ExzoNetwork/ExzoCoin/command/server" 16 | "github.com/ExzoNetwork/ExzoCoin/command/status" 17 | "github.com/ExzoNetwork/ExzoCoin/command/txpool" 18 | "github.com/ExzoNetwork/ExzoCoin/command/version" 19 | "github.com/ExzoNetwork/ExzoCoin/command/whitelist" 20 | "github.com/spf13/cobra" 21 | ) 22 | 23 | type RootCommand struct { 24 | baseCmd *cobra.Command 25 | } 26 | 27 | func NewRootCommand() *RootCommand { 28 | rootCommand := &RootCommand{ 29 | baseCmd: &cobra.Command{ 30 | Short: "Polygon Edge is a framework for building Ethereum-compatible Blockchain networks", 31 | }, 32 | } 33 | 34 | helper.RegisterJSONOutputFlag(rootCommand.baseCmd) 35 | 36 | rootCommand.registerSubCommands() 37 | 38 | return rootCommand 39 | } 40 | 41 | func (rc *RootCommand) registerSubCommands() { 42 | rc.baseCmd.AddCommand( 43 | version.GetCommand(), 44 | txpool.GetCommand(), 45 | status.GetCommand(), 46 | secrets.GetCommand(), 47 | peers.GetCommand(), 48 | monitor.GetCommand(), 49 | ibft.GetCommand(), 50 | backup.GetCommand(), 51 | genesis.GetCommand(), 52 | server.GetCommand(), 53 | whitelist.GetCommand(), 54 | license.GetCommand(), 55 | ) 56 | } 57 | 58 | func (rc *RootCommand) Execute() { 59 | if err := rc.baseCmd.Execute(); err != nil { 60 | _, _ = fmt.Fprintln(os.Stderr, err) 61 | 62 | os.Exit(1) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /command/secrets/generate/result.go: -------------------------------------------------------------------------------- 1 | package generate 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | type SecretsGenerateResult struct { 11 | ServiceType string `json:"service_type"` 12 | ServerURL string `json:"server_url"` 13 | AccessToken string `json:"access_token"` 14 | NodeName string `json:"node_name"` 15 | Namespace string `json:"namespace"` 16 | Extra string `json:"extra"` 17 | } 18 | 19 | func (r *SecretsGenerateResult) GetOutput() string { 20 | var buffer bytes.Buffer 21 | 22 | buffer.WriteString("\n[SECRETS GENERATE]\n") 23 | buffer.WriteString(helper.FormatKV([]string{ 24 | fmt.Sprintf("Service Type|%s", r.ServiceType), 25 | fmt.Sprintf("Server URL|%s", r.ServerURL), 26 | fmt.Sprintf("Access Token|%s", r.AccessToken), 27 | fmt.Sprintf("Node Name|%s", r.NodeName), 28 | fmt.Sprintf("Namespace|%s", r.Namespace), 29 | fmt.Sprintf("Extra|%s", r.Extra), 30 | })) 31 | buffer.WriteString("\n") 32 | 33 | return buffer.String() 34 | } 35 | -------------------------------------------------------------------------------- /command/secrets/generate/secrets_generate.go: -------------------------------------------------------------------------------- 1 | package generate 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | "github.com/spf13/cobra" 9 | 10 | "github.com/ExzoNetwork/ExzoCoin/secrets" 11 | ) 12 | 13 | func GetCommand() *cobra.Command { 14 | secretsGenerateCmd := &cobra.Command{ 15 | Use: "generate", 16 | Short: "Initializes the secrets manager configuration in the provided directory.", 17 | Run: runCommand, 18 | } 19 | 20 | setFlags(secretsGenerateCmd) 21 | helper.SetRequiredFlags(secretsGenerateCmd, params.getRequiredFlags()) 22 | 23 | return secretsGenerateCmd 24 | } 25 | 26 | func setFlags(cmd *cobra.Command) { 27 | cmd.Flags().StringVar( 28 | ¶ms.dir, 29 | dirFlag, 30 | defaultConfigFileName, 31 | "the directory for the secrets manager configuration file", 32 | ) 33 | 34 | cmd.Flags().StringVar( 35 | ¶ms.token, 36 | tokenFlag, 37 | "", 38 | "the access token for the service", 39 | ) 40 | 41 | cmd.Flags().StringVar( 42 | ¶ms.serverURL, 43 | serverURLFlag, 44 | "", 45 | "the server URL for the service", 46 | ) 47 | 48 | cmd.Flags().StringVar( 49 | ¶ms.serviceType, 50 | typeFlag, 51 | string(secrets.HashicorpVault), 52 | fmt.Sprintf( 53 | "the type of the secrets manager. Available types: %s, %s and %s", 54 | secrets.HashicorpVault, 55 | secrets.AWSSSM, 56 | secrets.GCPSSM, 57 | ), 58 | ) 59 | 60 | cmd.Flags().StringVar( 61 | ¶ms.name, 62 | nameFlag, 63 | defaultNodeName, 64 | "the name of the node for on-service record keeping", 65 | ) 66 | 67 | cmd.Flags().StringVar( 68 | ¶ms.namespace, 69 | namespaceFlag, 70 | defaultNamespace, 71 | "the namespace for the service", 72 | ) 73 | 74 | cmd.Flags().StringVar( 75 | ¶ms.extra, 76 | extraFlag, 77 | "", 78 | "Specifies the extra fields map in string format 'key1=val1,key2=val2'", 79 | ) 80 | } 81 | 82 | func runCommand(cmd *cobra.Command, _ []string) { 83 | outputter := command.InitializeOutputter(cmd) 84 | defer outputter.WriteOutput() 85 | 86 | if err := params.writeSecretsConfig(); err != nil { 87 | outputter.SetError(err) 88 | 89 | return 90 | } 91 | 92 | outputter.SetCommandResult(params.getResult()) 93 | } 94 | -------------------------------------------------------------------------------- /command/secrets/init/result.go: -------------------------------------------------------------------------------- 1 | package init 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command" 8 | 9 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 10 | "github.com/ExzoNetwork/ExzoCoin/types" 11 | ) 12 | 13 | type Results []command.CommandResult 14 | 15 | func (r Results) GetOutput() string { 16 | var buffer bytes.Buffer 17 | 18 | for _, result := range r { 19 | buffer.WriteString(result.GetOutput()) 20 | } 21 | 22 | return buffer.String() 23 | } 24 | 25 | type SecretsInitResult struct { 26 | Address types.Address `json:"address"` 27 | BLSPubkey string `json:"bls_pubkey"` 28 | NodeID string `json:"node_id"` 29 | } 30 | 31 | func (r *SecretsInitResult) GetOutput() string { 32 | var buffer bytes.Buffer 33 | 34 | vals := make([]string, 0, 3) 35 | 36 | vals = append( 37 | vals, 38 | fmt.Sprintf("Public key (address)|%s", r.Address.String()), 39 | ) 40 | 41 | if r.BLSPubkey != "" { 42 | vals = append( 43 | vals, 44 | fmt.Sprintf("BLS Public key|%s", r.BLSPubkey), 45 | ) 46 | } 47 | 48 | vals = append(vals, fmt.Sprintf("Node ID|%s", r.NodeID)) 49 | 50 | buffer.WriteString("\n[SECRETS INIT]\n") 51 | buffer.WriteString(helper.FormatKV(vals)) 52 | buffer.WriteString("\n") 53 | 54 | return buffer.String() 55 | } 56 | -------------------------------------------------------------------------------- /command/secrets/output/result.go: -------------------------------------------------------------------------------- 1 | package output 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | // SecretsOutputAllResults for default output case 11 | type SecretsOutputAllResult struct { 12 | Address string `json:"address"` 13 | BLSPubkey string `json:"bls"` 14 | NodeID string `json:"node_id"` 15 | } 16 | 17 | // SecretsOutputNodeIDResults for `--node` output case 18 | type SecretsOutputNodeIDResult struct { 19 | NodeID string `json:"node_id"` 20 | } 21 | 22 | // SecretsOutputBLSResults for `--bls` output case 23 | type SecretsOutputBLSResult struct { 24 | BLSPubkey string `json:"bls"` 25 | } 26 | 27 | // SecretsOutputValidatorResult for `--validator` output case 28 | type SecretsOutputValidatorResult struct { 29 | Address string `json:"address"` 30 | } 31 | 32 | func (r *SecretsOutputNodeIDResult) GetOutput() string { 33 | return r.NodeID 34 | } 35 | 36 | func (r *SecretsOutputValidatorResult) GetOutput() string { 37 | return r.Address 38 | } 39 | 40 | func (r *SecretsOutputBLSResult) GetOutput() string { 41 | return r.BLSPubkey 42 | } 43 | 44 | func (r *SecretsOutputAllResult) GetOutput() string { 45 | var buffer bytes.Buffer 46 | 47 | vals := make([]string, 0, 3) 48 | 49 | vals = append( 50 | vals, 51 | fmt.Sprintf("Public key (address)|%s", r.Address), 52 | ) 53 | 54 | vals = append( 55 | vals, 56 | fmt.Sprintf("BLS Public key|%s", r.BLSPubkey), 57 | ) 58 | 59 | vals = append( 60 | vals, 61 | fmt.Sprintf("Node ID|%s", r.NodeID), 62 | ) 63 | 64 | buffer.WriteString("\n[SECRETS OUTPUT]\n") 65 | buffer.WriteString(helper.FormatKV(vals)) 66 | 67 | buffer.WriteString("\n") 68 | 69 | return buffer.String() 70 | } 71 | -------------------------------------------------------------------------------- /command/secrets/output/secrets_output.go: -------------------------------------------------------------------------------- 1 | package output 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | func GetCommand() *cobra.Command { 9 | secretsOutputCmd := &cobra.Command{ 10 | Use: "output", 11 | Short: "Outputs validator key address and public network key from the provided Secrets Manager", 12 | PreRunE: runPreRun, 13 | Run: runCommand, 14 | } 15 | 16 | setFlags(secretsOutputCmd) 17 | 18 | return secretsOutputCmd 19 | } 20 | 21 | func setFlags(cmd *cobra.Command) { 22 | cmd.Flags().StringVar( 23 | ¶ms.dataDir, 24 | dataDirFlag, 25 | "", 26 | "the directory for the Polygon Edge data if the local FS is used", 27 | ) 28 | 29 | cmd.Flags().StringVar( 30 | ¶ms.configPath, 31 | configFlag, 32 | "", 33 | "the path to the SecretsManager config file, "+ 34 | "if omitted, the local FS secrets manager is used", 35 | ) 36 | 37 | cmd.Flags().BoolVar( 38 | ¶ms.outputBLS, 39 | blsFlag, 40 | false, 41 | "output only the BLS public key "+ 42 | "from the provided secrets manager", 43 | ) 44 | 45 | cmd.Flags().BoolVar( 46 | ¶ms.outputNodeID, 47 | nodeIDFlag, 48 | false, 49 | "output only the node id "+ 50 | "from the provided secrets manager", 51 | ) 52 | 53 | cmd.Flags().BoolVar( 54 | ¶ms.outputValidator, 55 | validatorFlag, 56 | false, 57 | "output only the validator key address "+ 58 | "from the provided secrets manager", 59 | ) 60 | 61 | cmd.MarkFlagsMutuallyExclusive(dataDirFlag, configFlag) 62 | cmd.MarkFlagsMutuallyExclusive(nodeIDFlag, validatorFlag, blsFlag) 63 | } 64 | 65 | func runPreRun(_ *cobra.Command, _ []string) error { 66 | return params.validateFlags() 67 | } 68 | 69 | func runCommand(cmd *cobra.Command, _ []string) { 70 | outputter := command.InitializeOutputter(cmd) 71 | defer outputter.WriteOutput() 72 | 73 | if err := params.initSecrets(); err != nil { 74 | outputter.SetError(err) 75 | 76 | return 77 | } 78 | 79 | outputter.SetCommandResult(params.getResult()) 80 | } 81 | -------------------------------------------------------------------------------- /command/secrets/secrets.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 5 | "github.com/ExzoNetwork/ExzoCoin/command/secrets/generate" 6 | initCmd "github.com/ExzoNetwork/ExzoCoin/command/secrets/init" 7 | "github.com/ExzoNetwork/ExzoCoin/command/secrets/output" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func GetCommand() *cobra.Command { 12 | secretsCmd := &cobra.Command{ 13 | Use: "secrets", 14 | Short: "Top level SecretsManager command for interacting with secrets functionality. Only accepts subcommands.", 15 | } 16 | 17 | helper.RegisterGRPCAddressFlag(secretsCmd) 18 | 19 | registerSubcommands(secretsCmd) 20 | 21 | return secretsCmd 22 | } 23 | 24 | func registerSubcommands(baseCmd *cobra.Command) { 25 | baseCmd.AddCommand( 26 | // secrets init 27 | initCmd.GetCommand(), 28 | // secrets generate 29 | generate.GetCommand(), 30 | // secrets output public data 31 | output.GetCommand(), 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /command/server/export/export.go: -------------------------------------------------------------------------------- 1 | package export 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/ExzoNetwork/ExzoCoin/command" 10 | "github.com/ExzoNetwork/ExzoCoin/command/server/config" 11 | "github.com/spf13/cobra" 12 | "gopkg.in/yaml.v3" 13 | ) 14 | 15 | func GetCommand() *cobra.Command { 16 | configCmd := &cobra.Command{ 17 | Use: "export", 18 | Short: "export default-config.yaml file with default parameters that can be used to run the server", 19 | Run: runGenerateConfigCommand, 20 | } 21 | 22 | setFlags(configCmd) 23 | 24 | return configCmd 25 | } 26 | 27 | func setFlags(cmd *cobra.Command) { 28 | cmd.Flags().StringVar( 29 | ¶mFlagValues.FileType, 30 | fileTypeFlag, 31 | "yaml", 32 | "file type of exported config file (yaml or json)", 33 | ) 34 | } 35 | 36 | func runGenerateConfigCommand(cmd *cobra.Command, _ []string) { 37 | outputter := command.InitializeOutputter(cmd) 38 | defer outputter.WriteOutput() 39 | 40 | if err := generateConfig(*config.DefaultConfig()); err != nil { 41 | outputter.SetError(err) 42 | 43 | return 44 | } 45 | 46 | outputter.SetCommandResult(&cmdResult{ 47 | CommandOutput: "Configuration file successfully exported", 48 | }) 49 | } 50 | 51 | func generateConfig(config config.Config) error { 52 | config.Network.MaxPeers = -1 53 | config.Network.MaxInboundPeers = -1 54 | config.Network.MaxOutboundPeers = -1 55 | 56 | var ( 57 | data []byte 58 | err error 59 | ) 60 | 61 | switch paramFlagValues.FileType { 62 | case "yaml", "yml": 63 | data, err = yaml.Marshal(config) 64 | case "json": 65 | data, err = json.MarshalIndent(config, "", " ") 66 | default: 67 | return errors.New("invalid file type, only yaml and json are supported") 68 | } 69 | 70 | if err != nil { 71 | return fmt.Errorf("could not marshal config struct, %w", err) 72 | } 73 | 74 | if err := os.WriteFile( 75 | fmt.Sprintf("default-config.%s", paramFlagValues.FileType), 76 | data, 77 | os.ModePerm); err != nil { 78 | return errors.New("could not create and write config file") 79 | } 80 | 81 | return nil 82 | } 83 | -------------------------------------------------------------------------------- /command/server/export/params.go: -------------------------------------------------------------------------------- 1 | package export 2 | 3 | const ( 4 | fileTypeFlag = "type" 5 | ) 6 | 7 | type exportParams struct { 8 | FileType string 9 | } 10 | 11 | var ( 12 | paramFlagValues = &exportParams{} 13 | ) 14 | -------------------------------------------------------------------------------- /command/server/export/result.go: -------------------------------------------------------------------------------- 1 | package export 2 | 3 | import "bytes" 4 | 5 | type cmdResult struct { 6 | CommandOutput string `json:"export_result"` 7 | } 8 | 9 | func (c *cmdResult) GetOutput() string { 10 | var buffer bytes.Buffer 11 | 12 | buffer.WriteString("\n[EXPORT SUCCESS]\n") 13 | buffer.WriteString(c.CommandOutput + "\n") 14 | 15 | return buffer.String() 16 | } 17 | -------------------------------------------------------------------------------- /command/status/result.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | type StatusResult struct { 11 | ChainID int64 `json:"chain_id"` 12 | CurrentBlockNumber int64 `json:"current_block_number"` 13 | CurrentBlockHash string `json:"current_block_hash"` 14 | LibP2PAddress string `json:"libp2p_address"` 15 | } 16 | 17 | func (r *StatusResult) GetOutput() string { 18 | var buffer bytes.Buffer 19 | 20 | buffer.WriteString("\n[CLIENT STATUS]\n") 21 | buffer.WriteString(helper.FormatKV([]string{ 22 | fmt.Sprintf("Network (Chain ID)|%d", r.ChainID), 23 | fmt.Sprintf("Current Block Number (base 10)|%d", r.CurrentBlockNumber), 24 | fmt.Sprintf("Current Block Hash|%s", r.CurrentBlockHash), 25 | fmt.Sprintf("Libp2p Address|%s", r.LibP2PAddress), 26 | })) 27 | 28 | return buffer.String() 29 | } 30 | -------------------------------------------------------------------------------- /command/status/status.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/golang/protobuf/ptypes/empty" 8 | "github.com/spf13/cobra" 9 | 10 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 11 | "github.com/ExzoNetwork/ExzoCoin/server/proto" 12 | ) 13 | 14 | func GetCommand() *cobra.Command { 15 | statusCmd := &cobra.Command{ 16 | Use: "status", 17 | Short: "Returns the status of the Polygon Edge client", 18 | Args: cobra.NoArgs, 19 | Run: runCommand, 20 | } 21 | 22 | helper.RegisterGRPCAddressFlag(statusCmd) 23 | 24 | return statusCmd 25 | } 26 | 27 | func runCommand(cmd *cobra.Command, _ []string) { 28 | outputter := command.InitializeOutputter(cmd) 29 | defer outputter.WriteOutput() 30 | 31 | statusResponse, err := getSystemStatus(helper.GetGRPCAddress(cmd)) 32 | if err != nil { 33 | outputter.SetError(err) 34 | 35 | return 36 | } 37 | 38 | outputter.SetCommandResult(&StatusResult{ 39 | ChainID: statusResponse.Network, 40 | CurrentBlockNumber: statusResponse.Current.Number, 41 | CurrentBlockHash: statusResponse.Current.Hash, 42 | LibP2PAddress: statusResponse.P2PAddr, 43 | }) 44 | } 45 | 46 | func getSystemStatus(grpcAddress string) (*proto.ServerStatus, error) { 47 | client, err := helper.GetSystemClientConnection( 48 | grpcAddress, 49 | ) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | return client.GetStatus(context.Background(), &empty.Empty{}) 55 | } 56 | -------------------------------------------------------------------------------- /command/txpool/status/result.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | type TxPoolStatusResult struct { 11 | Transactions uint64 `json:"transactions"` 12 | } 13 | 14 | func (r *TxPoolStatusResult) GetOutput() string { 15 | var buffer bytes.Buffer 16 | 17 | buffer.WriteString("\n[TXPOOL STATUS]\n") 18 | buffer.WriteString(helper.FormatKV([]string{ 19 | fmt.Sprintf("Number of transactions in pool:|%d", r.Transactions), 20 | })) 21 | buffer.WriteString("\n") 22 | 23 | return buffer.String() 24 | } 25 | -------------------------------------------------------------------------------- /command/txpool/status/txpool_status.go: -------------------------------------------------------------------------------- 1 | package status 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | "github.com/spf13/cobra" 9 | 10 | txpoolOp "github.com/ExzoNetwork/ExzoCoin/txpool/proto" 11 | empty "google.golang.org/protobuf/types/known/emptypb" 12 | ) 13 | 14 | func GetCommand() *cobra.Command { 15 | return &cobra.Command{ 16 | Use: "status", 17 | Short: "Returns the number of transactions in the transaction pool", 18 | Run: runCommand, 19 | } 20 | } 21 | 22 | func runCommand(cmd *cobra.Command, _ []string) { 23 | outputter := command.InitializeOutputter(cmd) 24 | defer outputter.WriteOutput() 25 | 26 | statusResponse, err := getTxPoolStatus(helper.GetGRPCAddress(cmd)) 27 | if err != nil { 28 | outputter.SetError(err) 29 | 30 | return 31 | } 32 | 33 | outputter.SetCommandResult(&TxPoolStatusResult{ 34 | Transactions: statusResponse.Length, 35 | }) 36 | } 37 | 38 | func getTxPoolStatus(grpcAddress string) (*txpoolOp.TxnPoolStatusResp, error) { 39 | client, err := helper.GetTxPoolClientConnection( 40 | grpcAddress, 41 | ) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | return client.Status(context.Background(), &empty.Empty{}) 47 | } 48 | -------------------------------------------------------------------------------- /command/txpool/subscribe/params.go: -------------------------------------------------------------------------------- 1 | package subscribe 2 | 3 | import "github.com/ExzoNetwork/ExzoCoin/txpool/proto" 4 | 5 | var ( 6 | params = &subscribeParams{} 7 | ) 8 | 9 | var ( 10 | addedFlag = "added" 11 | promotedFlag = "promoted" 12 | demotedFlag = "demoted" 13 | enqueuedFlag = "enqueued" 14 | droppedFlag = "dropped" 15 | prunedPromotedFlag = "pruned-promoted" 16 | prunedEnqueuedFlag = "pruned-enqueued" 17 | ) 18 | 19 | type subscribeParams struct { 20 | eventSubscriptionMap map[proto.EventType]*bool 21 | supportedEvents []proto.EventType 22 | } 23 | 24 | func (sp *subscribeParams) initEventMap() { 25 | falseRaw := false 26 | sp.eventSubscriptionMap = map[proto.EventType]*bool{ 27 | proto.EventType_ADDED: &falseRaw, 28 | proto.EventType_ENQUEUED: &falseRaw, 29 | proto.EventType_PROMOTED: &falseRaw, 30 | proto.EventType_DROPPED: &falseRaw, 31 | proto.EventType_DEMOTED: &falseRaw, 32 | proto.EventType_PRUNED_PROMOTED: &falseRaw, 33 | proto.EventType_PRUNED_ENQUEUED: &falseRaw, 34 | } 35 | } 36 | 37 | func (sp *subscribeParams) init() { 38 | sp.setSpecifiedEvents() 39 | 40 | if !sp.areEventsSpecified() { 41 | sp.setAllEvents() 42 | } 43 | } 44 | 45 | func (sp *subscribeParams) setSpecifiedEvents() { 46 | sp.supportedEvents = make([]proto.EventType, 0) 47 | 48 | for eventType, eventIsSupported := range sp.eventSubscriptionMap { 49 | if *eventIsSupported { 50 | sp.supportedEvents = append(sp.supportedEvents, eventType) 51 | } 52 | } 53 | } 54 | 55 | func (sp *subscribeParams) areEventsSpecified() bool { 56 | return len(sp.supportedEvents) != 0 57 | } 58 | 59 | func (sp *subscribeParams) setAllEvents() { 60 | sp.supportedEvents = []proto.EventType{ 61 | proto.EventType_ADDED, 62 | proto.EventType_ENQUEUED, 63 | proto.EventType_PROMOTED, 64 | proto.EventType_DROPPED, 65 | proto.EventType_DEMOTED, 66 | proto.EventType_PRUNED_PROMOTED, 67 | proto.EventType_PRUNED_ENQUEUED, 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /command/txpool/subscribe/result.go: -------------------------------------------------------------------------------- 1 | package subscribe 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | txpoolProto "github.com/ExzoNetwork/ExzoCoin/txpool/proto" 9 | ) 10 | 11 | type TxPoolEventResult struct { 12 | EventType txpoolProto.EventType `json:"event_type"` 13 | TxHash string `json:"tx_hash"` 14 | } 15 | 16 | func (r *TxPoolEventResult) GetOutput() string { 17 | var buffer bytes.Buffer 18 | 19 | buffer.WriteString("\n[TXPOOL EVENT]\n") 20 | buffer.WriteString(helper.FormatKV([]string{ 21 | fmt.Sprintf("TYPE|%s", r.EventType), 22 | fmt.Sprintf("HASH|%s", r.TxHash), 23 | })) 24 | buffer.WriteString("\n") 25 | 26 | return buffer.String() 27 | } 28 | -------------------------------------------------------------------------------- /command/txpool/txpool.go: -------------------------------------------------------------------------------- 1 | package txpool 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 5 | "github.com/ExzoNetwork/ExzoCoin/command/txpool/status" 6 | "github.com/ExzoNetwork/ExzoCoin/command/txpool/subscribe" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func GetCommand() *cobra.Command { 11 | txPoolCmd := &cobra.Command{ 12 | Use: "txpool", 13 | Short: "Top level command for interacting with the transaction pool. Only accepts subcommands.", 14 | } 15 | 16 | helper.RegisterGRPCAddressFlag(txPoolCmd) 17 | 18 | registerSubcommands(txPoolCmd) 19 | 20 | return txPoolCmd 21 | } 22 | 23 | func registerSubcommands(baseCmd *cobra.Command) { 24 | baseCmd.AddCommand( 25 | // txpool status 26 | status.GetCommand(), 27 | // txpool subscribe 28 | subscribe.GetCommand(), 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /command/version/result.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/command/helper" 8 | ) 9 | 10 | type VersionResult struct { 11 | Version string `json:"version"` 12 | Commit string `json:"commit"` 13 | Branch string `json:"branch"` 14 | BuildTime string `json:"buildTime"` 15 | } 16 | 17 | func (r *VersionResult) GetOutput() string { 18 | var buffer bytes.Buffer 19 | 20 | buffer.WriteString("\n[VERSION INFO]\n") 21 | buffer.WriteString(helper.FormatKV([]string{ 22 | fmt.Sprintf("Release version|%s", r.Version), 23 | fmt.Sprintf("Git branch|%s", r.Branch), 24 | fmt.Sprintf("Commit hash|%s", r.Commit), 25 | fmt.Sprintf("Build time|%s", r.BuildTime), 26 | })) 27 | 28 | return buffer.String() 29 | } 30 | -------------------------------------------------------------------------------- /command/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command" 5 | "github.com/ExzoNetwork/ExzoCoin/versioning" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func GetCommand() *cobra.Command { 10 | return &cobra.Command{ 11 | Use: "version", 12 | Short: "Returns the current Polygon Edge version", 13 | Args: cobra.NoArgs, 14 | Run: runCommand, 15 | } 16 | } 17 | 18 | func runCommand(cmd *cobra.Command, _ []string) { 19 | outputter := command.InitializeOutputter(cmd) 20 | defer outputter.WriteOutput() 21 | 22 | outputter.SetCommandResult( 23 | &VersionResult{ 24 | Version: versioning.Version, 25 | Commit: versioning.Commit, 26 | Branch: versioning.Branch, 27 | BuildTime: versioning.BuildTime, 28 | }, 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /command/whitelist/deployment/deployment.go: -------------------------------------------------------------------------------- 1 | package deployment 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func GetCommand() *cobra.Command { 11 | deploymentCmd := &cobra.Command{ 12 | Use: "deployment", 13 | Short: "Top level command for updating smart contract deployment whitelist. Only accepts subcommands", 14 | PreRunE: runPreRun, 15 | Run: runCommand, 16 | } 17 | 18 | setFlags(deploymentCmd) 19 | 20 | return deploymentCmd 21 | } 22 | 23 | func setFlags(cmd *cobra.Command) { 24 | cmd.Flags().StringVar( 25 | ¶ms.genesisPath, 26 | chainFlag, 27 | fmt.Sprintf("./%s", command.DefaultGenesisFileName), 28 | "the genesis file to update", 29 | ) 30 | cmd.Flags().StringArrayVar( 31 | ¶ms.addAddressRaw, 32 | addAddressFlag, 33 | []string{}, 34 | "adds a new address to the contract deployment whitelist", 35 | ) 36 | 37 | cmd.Flags().StringArrayVar( 38 | ¶ms.removeAddressRaw, 39 | removeAddressFlag, 40 | []string{}, 41 | "removes a new address from the contract deployment whitelist", 42 | ) 43 | } 44 | 45 | func runPreRun(_ *cobra.Command, _ []string) error { 46 | return params.initRawParams() 47 | } 48 | 49 | func runCommand(cmd *cobra.Command, _ []string) { 50 | outputter := command.InitializeOutputter(cmd) 51 | defer outputter.WriteOutput() 52 | 53 | if err := params.updateGenesisConfig(); err != nil { 54 | outputter.SetError(err) 55 | 56 | return 57 | } 58 | 59 | if err := params.overrideGenesisConfig(); err != nil { 60 | outputter.SetError(err) 61 | 62 | return 63 | } 64 | 65 | outputter.SetCommandResult(params.getResult()) 66 | } 67 | -------------------------------------------------------------------------------- /command/whitelist/deployment/result.go: -------------------------------------------------------------------------------- 1 | package deployment 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/types" 8 | ) 9 | 10 | type DeploymentResult struct { 11 | AddAddresses []types.Address `json:"addAddress,omitempty"` 12 | RemoveAddresses []types.Address `json:"removeAddress,omitempty"` 13 | Whitelist []types.Address `json:"whitelist"` 14 | } 15 | 16 | func (r *DeploymentResult) GetOutput() string { 17 | var buffer bytes.Buffer 18 | 19 | buffer.WriteString("\n[CONTRACT DEPLOYMENT WHITELIST]\n\n") 20 | 21 | if len(r.AddAddresses) != 0 { 22 | buffer.WriteString(fmt.Sprintf("Added addresses: %s,\n", r.AddAddresses)) 23 | } 24 | 25 | if len(r.RemoveAddresses) != 0 { 26 | buffer.WriteString(fmt.Sprintf("Removed addresses: %s,\n", r.RemoveAddresses)) 27 | } 28 | 29 | buffer.WriteString(fmt.Sprintf("Contract deployment whitelist : %s,\n", r.Whitelist)) 30 | 31 | return buffer.String() 32 | } 33 | -------------------------------------------------------------------------------- /command/whitelist/show/params.go: -------------------------------------------------------------------------------- 1 | package show 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/chain" 7 | "github.com/ExzoNetwork/ExzoCoin/command" 8 | "github.com/ExzoNetwork/ExzoCoin/helper/config" 9 | "github.com/ExzoNetwork/ExzoCoin/types" 10 | ) 11 | 12 | const ( 13 | chainFlag = "chain" 14 | ) 15 | 16 | var ( 17 | params = &showParams{} 18 | ) 19 | 20 | type showParams struct { 21 | // genesis file path 22 | genesisPath string 23 | 24 | // deployment whitelist 25 | whitelists Whitelists 26 | } 27 | 28 | type Whitelists struct { 29 | deployment []types.Address 30 | } 31 | 32 | func (p *showParams) initRawParams() error { 33 | // init genesis configuration 34 | if err := p.initWhitelists(); err != nil { 35 | return err 36 | } 37 | 38 | return nil 39 | } 40 | 41 | func (p *showParams) initWhitelists() error { 42 | // import genesis configuration 43 | genesisConfig, err := chain.Import(p.genesisPath) 44 | if err != nil { 45 | return fmt.Errorf( 46 | "failed to load chain config from %s: %w", 47 | p.genesisPath, 48 | err, 49 | ) 50 | } 51 | 52 | // fetch whitelists 53 | deploymentWhitelist, err := config.GetDeploymentWhitelist(genesisConfig) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | // set whitelists 59 | p.whitelists = Whitelists{ 60 | deployment: deploymentWhitelist, 61 | } 62 | 63 | return nil 64 | } 65 | 66 | func (p *showParams) getResult() command.CommandResult { 67 | result := &ShowResult{ 68 | Whitelists: p.whitelists, 69 | } 70 | 71 | return result 72 | } 73 | -------------------------------------------------------------------------------- /command/whitelist/show/result.go: -------------------------------------------------------------------------------- 1 | package show 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | type ShowResult struct { 9 | Whitelists Whitelists 10 | } 11 | 12 | func (r *ShowResult) GetOutput() string { 13 | var buffer bytes.Buffer 14 | 15 | buffer.WriteString("\n[WHITELISTS]\n\n") 16 | 17 | buffer.WriteString(fmt.Sprintf("Contract deployment whitelist : %s,\n", r.Whitelists.deployment)) 18 | 19 | return buffer.String() 20 | } 21 | -------------------------------------------------------------------------------- /command/whitelist/show/show.go: -------------------------------------------------------------------------------- 1 | package show 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func GetCommand() *cobra.Command { 11 | showCmd := &cobra.Command{ 12 | Use: "show", 13 | Short: "Displays whitelist information", 14 | PreRunE: runPreRun, 15 | Run: runCommand, 16 | } 17 | 18 | setFlags(showCmd) 19 | 20 | return showCmd 21 | } 22 | 23 | func setFlags(cmd *cobra.Command) { 24 | cmd.Flags().StringVar( 25 | ¶ms.genesisPath, 26 | chainFlag, 27 | fmt.Sprintf("./%s", command.DefaultGenesisFileName), 28 | "the genesis file to update", 29 | ) 30 | } 31 | 32 | func runPreRun(_ *cobra.Command, _ []string) error { 33 | return params.initRawParams() 34 | } 35 | 36 | func runCommand(cmd *cobra.Command, _ []string) { 37 | outputter := command.InitializeOutputter(cmd) 38 | defer outputter.WriteOutput() 39 | 40 | outputter.SetCommandResult(params.getResult()) 41 | } 42 | -------------------------------------------------------------------------------- /command/whitelist/whitelist.go: -------------------------------------------------------------------------------- 1 | package whitelist 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/command/whitelist/deployment" 5 | "github.com/ExzoNetwork/ExzoCoin/command/whitelist/show" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func GetCommand() *cobra.Command { 10 | whitelistCmd := &cobra.Command{ 11 | Use: "whitelist", 12 | Short: "Top level command for modifying the Polygon Edge whitelists within the config. Only accepts subcommands.", 13 | } 14 | 15 | registerSubcommands(whitelistCmd) 16 | 17 | return whitelistCmd 18 | } 19 | 20 | func registerSubcommands(baseCmd *cobra.Command) { 21 | baseCmd.AddCommand( 22 | deployment.GetCommand(), 23 | show.GetCommand(), 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /consensus/dummy/dummy.go: -------------------------------------------------------------------------------- 1 | package dummy 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/blockchain" 5 | "github.com/ExzoNetwork/ExzoCoin/consensus" 6 | "github.com/ExzoNetwork/ExzoCoin/helper/progress" 7 | "github.com/ExzoNetwork/ExzoCoin/state" 8 | "github.com/ExzoNetwork/ExzoCoin/txpool" 9 | "github.com/ExzoNetwork/ExzoCoin/types" 10 | "github.com/hashicorp/go-hclog" 11 | ) 12 | 13 | type Dummy struct { 14 | logger hclog.Logger 15 | notifyCh chan struct{} 16 | closeCh chan struct{} 17 | txpool *txpool.TxPool 18 | blockchain *blockchain.Blockchain 19 | executor *state.Executor 20 | } 21 | 22 | func Factory(params *consensus.Params) (consensus.Consensus, error) { 23 | logger := params.Logger.Named("dummy") 24 | 25 | d := &Dummy{ 26 | logger: logger, 27 | notifyCh: make(chan struct{}), 28 | closeCh: make(chan struct{}), 29 | blockchain: params.Blockchain, 30 | executor: params.Executor, 31 | txpool: params.TxPool, 32 | } 33 | 34 | return d, nil 35 | } 36 | 37 | // Initialize initializes the consensus 38 | func (d *Dummy) Initialize() error { 39 | d.txpool.SetSealing(true) 40 | 41 | return nil 42 | } 43 | 44 | func (d *Dummy) Start() error { 45 | go d.run() 46 | 47 | return nil 48 | } 49 | 50 | func (d *Dummy) VerifyHeader(header *types.Header) error { 51 | // All blocks are valid 52 | return nil 53 | } 54 | 55 | func (d *Dummy) ProcessHeaders(headers []*types.Header) error { 56 | return nil 57 | } 58 | 59 | func (d *Dummy) GetBlockCreator(header *types.Header) (types.Address, error) { 60 | return types.BytesToAddress(header.Miner), nil 61 | } 62 | 63 | // PreCommitState a hook to be called before finalizing state transition on inserting block 64 | func (d *Dummy) PreCommitState(_header *types.Header, _txn *state.Transition) error { 65 | return nil 66 | } 67 | 68 | func (d *Dummy) GetSyncProgression() *progress.Progression { 69 | return nil 70 | } 71 | 72 | func (d *Dummy) Close() error { 73 | close(d.closeCh) 74 | 75 | return nil 76 | } 77 | 78 | func (d *Dummy) run() { 79 | d.logger.Info("started") 80 | // do nothing 81 | <-d.closeCh 82 | } 83 | -------------------------------------------------------------------------------- /consensus/ibft/consensus.go: -------------------------------------------------------------------------------- 1 | package ibft 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "github.com/0xPolygon/go-ibft/core" 8 | ) 9 | 10 | // IBFTConsensus is a convenience wrapper for the go-ibft package 11 | type IBFTConsensus struct { 12 | *core.IBFT 13 | 14 | wg sync.WaitGroup 15 | 16 | cancelSequence context.CancelFunc 17 | } 18 | 19 | func newIBFT( 20 | logger core.Logger, 21 | backend core.Backend, 22 | transport core.Transport, 23 | ) *IBFTConsensus { 24 | return &IBFTConsensus{ 25 | IBFT: core.NewIBFT(logger, backend, transport), 26 | wg: sync.WaitGroup{}, 27 | } 28 | } 29 | 30 | // runSequence starts the underlying consensus mechanism for the given height. 31 | // It may be called by a single thread at any given time 32 | func (c *IBFTConsensus) runSequence(height uint64) <-chan struct{} { 33 | done := make(chan struct{}) 34 | ctx, cancel := context.WithCancel(context.Background()) 35 | 36 | c.cancelSequence = cancel 37 | 38 | c.wg.Add(1) 39 | 40 | go func() { 41 | defer func() { 42 | cancel() 43 | c.wg.Done() 44 | close(done) 45 | }() 46 | 47 | c.RunSequence(ctx, height) 48 | }() 49 | 50 | return done 51 | } 52 | 53 | // stopSequence terminates the running IBFT sequence gracefully and waits for it to return 54 | func (c *IBFTConsensus) stopSequence() { 55 | c.cancelSequence() 56 | c.wg.Wait() 57 | } 58 | -------------------------------------------------------------------------------- /consensus/ibft/fork/helper.go: -------------------------------------------------------------------------------- 1 | package fork 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/validators/store/snapshot" 8 | ) 9 | 10 | // loadSnapshotMetadata loads Metadata from file 11 | func loadSnapshotMetadata(path string) (*snapshot.SnapshotMetadata, error) { 12 | var meta *snapshot.SnapshotMetadata 13 | if err := readDataStore(path, &meta); err != nil { 14 | return nil, err 15 | } 16 | 17 | return meta, nil 18 | } 19 | 20 | // loadSnapshots loads Snapshots from file 21 | func loadSnapshots(path string) ([]*snapshot.Snapshot, error) { 22 | snaps := []*snapshot.Snapshot{} 23 | if err := readDataStore(path, &snaps); err != nil { 24 | return nil, err 25 | } 26 | 27 | return snaps, nil 28 | } 29 | 30 | // readDataStore attempts to read the specific file from file storage 31 | // return nil if the file doesn't exist 32 | func readDataStore(path string, obj interface{}) error { 33 | if _, err := os.Stat(path); os.IsNotExist(err) { 34 | return nil 35 | } 36 | 37 | data, err := os.ReadFile(path) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | if err := json.Unmarshal(data, obj); err != nil { 43 | return err 44 | } 45 | 46 | return nil 47 | } 48 | 49 | // writeDataStore attempts to write the specific file to file storage 50 | func writeDataStore(path string, obj interface{}) error { 51 | data, err := json.Marshal(obj) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | if err := os.WriteFile(path, data, os.ModePerm); err != nil { 57 | return err 58 | } 59 | 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /consensus/ibft/fork/type.go: -------------------------------------------------------------------------------- 1 | package fork 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/validators/store" 7 | ) 8 | 9 | // Define the type of the IBFT consensus 10 | type IBFTType string 11 | 12 | const ( 13 | // PoA defines the Proof of Authority IBFT type, 14 | // where the validator set is changed through voting / pre-set in genesis 15 | PoA IBFTType = "PoA" 16 | 17 | // PoS defines the Proof of Stake IBFT type, 18 | // where the validator set it changed through staking on the Staking Smart Contract 19 | PoS IBFTType = "PoS" 20 | ) 21 | 22 | // ibftTypes is the map used for easy string -> IBFTType lookups 23 | var ibftTypes = map[string]IBFTType{ 24 | "PoA": PoA, 25 | "PoS": PoS, 26 | } 27 | 28 | // ibftTypesToSourceType defines validator set type used under each IBFT Type 29 | // Right now each IBFT Type is correspond one-to-one with ValidatorStore 30 | // In other words, PoA always uses SnapshotValidatorStore while PoS uses ContractValidatorStore 31 | // By definition, PoA can fetch validators from ContractValidatorStore 32 | var ibftTypesToSourceType = map[IBFTType]store.SourceType{ 33 | PoA: store.Snapshot, 34 | PoS: store.Contract, 35 | } 36 | 37 | // String is a helper method for casting a IBFTType to a string representation 38 | func (t IBFTType) String() string { 39 | return string(t) 40 | } 41 | 42 | // ParseIBFTType converts a ibftType string representation to a IBFTType 43 | func ParseIBFTType(ibftType string) (IBFTType, error) { 44 | // Check if the cast is possible 45 | castType, ok := ibftTypes[ibftType] 46 | if !ok { 47 | return castType, fmt.Errorf("invalid IBFT type %s", ibftType) 48 | } 49 | 50 | return castType, nil 51 | } 52 | -------------------------------------------------------------------------------- /consensus/ibft/fork/type_test.go: -------------------------------------------------------------------------------- 1 | package fork 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | testHelper "github.com/ExzoNetwork/ExzoCoin/helper/tests" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestIBFTTypeString(t *testing.T) { 12 | t.Parallel() 13 | 14 | cases := map[IBFTType]string{ 15 | PoA: "PoA", 16 | PoS: "PoS", 17 | } 18 | 19 | for typ, expected := range cases { 20 | assert.Equal( 21 | t, 22 | expected, 23 | typ.String(), 24 | ) 25 | } 26 | } 27 | 28 | func TestParseIBFTType(t *testing.T) { 29 | t.Parallel() 30 | 31 | tests := []struct { 32 | value string 33 | res IBFTType 34 | err error 35 | }{ 36 | { 37 | value: "PoA", 38 | res: PoA, 39 | err: nil, 40 | }, 41 | { 42 | value: "PoS", 43 | res: PoS, 44 | err: nil, 45 | }, 46 | { 47 | value: "hoge", 48 | res: IBFTType(""), 49 | err: errors.New("invalid IBFT type hoge"), 50 | }, 51 | } 52 | 53 | for _, test := range tests { 54 | test := test 55 | 56 | t.Run(test.value, func(t *testing.T) { 57 | t.Parallel() 58 | 59 | res, err := ParseIBFTType(test.value) 60 | 61 | assert.Equal( 62 | t, 63 | test.res, 64 | res, 65 | ) 66 | 67 | testHelper.AssertErrorMessageContains( 68 | t, 69 | test.err, 70 | err, 71 | ) 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /consensus/ibft/helper_test.go: -------------------------------------------------------------------------------- 1 | package ibft 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/ExzoNetwork/ExzoCoin/crypto" 9 | "github.com/ExzoNetwork/ExzoCoin/types" 10 | "github.com/ExzoNetwork/ExzoCoin/validators" 11 | ) 12 | 13 | type testerAccount struct { 14 | alias string 15 | priv *ecdsa.PrivateKey 16 | } 17 | 18 | func (t *testerAccount) Address() types.Address { 19 | return crypto.PubKeyToAddress(&t.priv.PublicKey) 20 | } 21 | 22 | type testerAccountPool struct { 23 | t *testing.T 24 | accounts []*testerAccount 25 | } 26 | 27 | func newTesterAccountPool(t *testing.T, num ...int) *testerAccountPool { 28 | t.Helper() 29 | 30 | pool := &testerAccountPool{ 31 | t: t, 32 | accounts: []*testerAccount{}, 33 | } 34 | 35 | if len(num) == 1 { 36 | for i := 0; i < num[0]; i++ { 37 | key, _ := crypto.GenerateECDSAKey() 38 | 39 | pool.accounts = append(pool.accounts, &testerAccount{ 40 | alias: strconv.Itoa(i), 41 | priv: key, 42 | }) 43 | } 44 | } 45 | 46 | return pool 47 | } 48 | 49 | func (ap *testerAccountPool) add(accounts ...string) { 50 | ap.t.Helper() 51 | 52 | for _, account := range accounts { 53 | if acct := ap.get(account); acct != nil { 54 | continue 55 | } 56 | 57 | priv, err := crypto.GenerateECDSAKey() 58 | if err != nil { 59 | panic("BUG: Failed to generate crypto key") 60 | } 61 | 62 | ap.accounts = append(ap.accounts, &testerAccount{ 63 | alias: account, 64 | priv: priv, 65 | }) 66 | } 67 | } 68 | 69 | func (ap *testerAccountPool) get(name string) *testerAccount { 70 | ap.t.Helper() 71 | 72 | for _, i := range ap.accounts { 73 | if i.alias == name { 74 | return i 75 | } 76 | } 77 | 78 | return nil 79 | } 80 | 81 | func (ap *testerAccountPool) ValidatorSet() validators.Validators { 82 | ap.t.Helper() 83 | 84 | v := validators.NewECDSAValidatorSet() 85 | for _, i := range ap.accounts { 86 | _ = v.Add(&validators.ECDSAValidator{ 87 | Address: i.Address(), 88 | }) 89 | } 90 | 91 | return v 92 | } 93 | -------------------------------------------------------------------------------- /consensus/ibft/proto/ibft_operator.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package v1; 4 | 5 | option go_package = "/consensus/ibft/proto"; 6 | 7 | import "google/protobuf/empty.proto"; 8 | 9 | service IbftOperator { 10 | rpc GetSnapshot(SnapshotReq) returns (Snapshot); 11 | rpc Propose(Candidate) returns (google.protobuf.Empty); 12 | rpc Candidates(google.protobuf.Empty) returns (CandidatesResp); 13 | rpc Status(google.protobuf.Empty) returns (IbftStatusResp); 14 | } 15 | 16 | message IbftStatusResp { 17 | string key = 1; 18 | } 19 | 20 | message SnapshotReq { 21 | bool latest = 1; 22 | uint64 number = 2; 23 | } 24 | 25 | message Snapshot { 26 | repeated Validator validators = 1; 27 | 28 | uint64 number = 2; 29 | 30 | string hash = 3; 31 | 32 | repeated Vote votes = 4; 33 | 34 | message Validator { 35 | string type = 1; 36 | string address = 2; 37 | bytes data = 3; 38 | } 39 | 40 | message Vote { 41 | string validator = 1; 42 | string proposed = 2; 43 | bool auth = 3; 44 | } 45 | } 46 | 47 | message ProposeReq { 48 | string address = 1; 49 | bool auth = 2; 50 | } 51 | 52 | message CandidatesResp { 53 | repeated Candidate candidates = 1; 54 | } 55 | 56 | message Candidate { 57 | string address = 1; 58 | bytes bls_pubkey = 2; 59 | bool auth = 3; 60 | } 61 | -------------------------------------------------------------------------------- /consensus/ibft/signer/key_manager.go: -------------------------------------------------------------------------------- 1 | package signer 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/types" 5 | "github.com/ExzoNetwork/ExzoCoin/validators" 6 | ) 7 | 8 | // KeyManager is a delegated module that signs data 9 | type KeyManager interface { 10 | // Type returns Validator type signer supports 11 | Type() validators.ValidatorType 12 | // Address returns an address of signer 13 | Address() types.Address 14 | // NewEmptyValidators creates empty validator collection the Signer expects 15 | NewEmptyValidators() validators.Validators 16 | // NewEmptyCommittedSeals creates empty committed seals the Signer expects 17 | NewEmptyCommittedSeals() Seals 18 | // SignProposerSeal creates a signature for ProposerSeal 19 | SignProposerSeal(hash []byte) ([]byte, error) 20 | // SignCommittedSeal creates a signature for committed seal 21 | SignCommittedSeal(hash []byte) ([]byte, error) 22 | // VerifyCommittedSeal verifies a committed seal 23 | VerifyCommittedSeal(vals validators.Validators, signer types.Address, sig, hash []byte) error 24 | // GenerateCommittedSeals creates CommittedSeals from committed seals 25 | GenerateCommittedSeals(sealsByValidator map[types.Address][]byte, vals validators.Validators) (Seals, error) 26 | // VerifyCommittedSeals verifies CommittedSeals 27 | VerifyCommittedSeals(seals Seals, hash []byte, vals validators.Validators) (int, error) 28 | // SignIBFTMessage signs for arbitrary bytes message 29 | SignIBFTMessage(msg []byte) ([]byte, error) 30 | // Ecrecover recovers address from signature and message 31 | Ecrecover(sig []byte, msg []byte) (types.Address, error) 32 | } 33 | -------------------------------------------------------------------------------- /consensus/ibft/state_test.go: -------------------------------------------------------------------------------- 1 | package ibft 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestState_FaultyNodes(t *testing.T) { 11 | cases := []struct { 12 | Network, Faulty uint64 13 | }{ 14 | {1, 0}, 15 | {2, 0}, 16 | {3, 0}, 17 | {4, 1}, 18 | {5, 1}, 19 | {6, 1}, 20 | {7, 2}, 21 | {8, 2}, 22 | {9, 2}, 23 | } 24 | for _, c := range cases { 25 | pool := newTesterAccountPool(t, int(c.Network)) 26 | vals := pool.ValidatorSet() 27 | assert.Equal(t, CalcMaxFaultyNodes(vals), int(c.Faulty)) 28 | } 29 | } 30 | 31 | // TestNumValid checks if the quorum size is calculated 32 | // correctly based on number of validators (network size). 33 | func TestNumValid(t *testing.T) { 34 | cases := []struct { 35 | Network, Quorum uint64 36 | }{ 37 | {1, 1}, 38 | {2, 2}, 39 | {3, 3}, 40 | {4, 3}, 41 | {5, 4}, 42 | {6, 4}, 43 | {7, 5}, 44 | {8, 6}, 45 | {9, 6}, 46 | } 47 | 48 | addAccounts := func( 49 | pool *testerAccountPool, 50 | numAccounts int, 51 | ) { 52 | // add accounts 53 | for i := 0; i < numAccounts; i++ { 54 | pool.add(strconv.Itoa(i)) 55 | } 56 | } 57 | 58 | for _, c := range cases { 59 | pool := newTesterAccountPool(t, int(c.Network)) 60 | addAccounts(pool, int(c.Network)) 61 | 62 | assert.Equal(t, 63 | int(c.Quorum), 64 | OptimalQuorumSize(pool.ValidatorSet()), 65 | ) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /consensus/ibft/transport.go: -------------------------------------------------------------------------------- 1 | package ibft 2 | 3 | import ( 4 | "github.com/0xPolygon/go-ibft/messages/proto" 5 | "github.com/ExzoNetwork/ExzoCoin/network" 6 | "github.com/ExzoNetwork/ExzoCoin/types" 7 | "github.com/libp2p/go-libp2p/core/peer" 8 | ) 9 | 10 | type transport interface { 11 | Multicast(msg *proto.Message) error 12 | } 13 | 14 | type gossipTransport struct { 15 | topic *network.Topic 16 | } 17 | 18 | func (g *gossipTransport) Multicast(msg *proto.Message) error { 19 | return g.topic.Publish(msg) 20 | } 21 | 22 | func (i *backendIBFT) Multicast(msg *proto.Message) { 23 | if err := i.transport.Multicast(msg); err != nil { 24 | i.logger.Error("fail to gossip", "err", err) 25 | } 26 | } 27 | 28 | // setupTransport sets up the gossip transport protocol 29 | func (i *backendIBFT) setupTransport() error { 30 | // Define a new topic 31 | topic, err := i.network.NewTopic(ibftProto, &proto.Message{}) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | // Subscribe to the newly created topic 37 | if err := topic.Subscribe( 38 | func(obj interface{}, _ peer.ID) { 39 | if !i.isActiveValidator() { 40 | return 41 | } 42 | 43 | msg, ok := obj.(*proto.Message) 44 | if !ok { 45 | i.logger.Error("invalid type assertion for message request") 46 | 47 | return 48 | } 49 | 50 | i.consensus.AddMessage(msg) 51 | 52 | i.logger.Debug( 53 | "validator message received", 54 | "type", msg.Type.String(), 55 | "height", msg.GetView().Height, 56 | "round", msg.GetView().Round, 57 | "addr", types.BytesToAddress(msg.From).String(), 58 | ) 59 | }, 60 | ); err != nil { 61 | return err 62 | } 63 | 64 | i.transport = &gossipTransport{topic: topic} 65 | 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /consensus/ibft/validators.go: -------------------------------------------------------------------------------- 1 | package ibft 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/types" 7 | "github.com/ExzoNetwork/ExzoCoin/validators" 8 | ) 9 | 10 | func CalcMaxFaultyNodes(s validators.Validators) int { 11 | // N -> number of nodes in IBFT 12 | // F -> number of faulty nodes 13 | // 14 | // N = 3F + 1 15 | // => F = (N - 1) / 3 16 | // 17 | // IBFT tolerates 1 failure with 4 nodes 18 | // 4 = 3 * 1 + 1 19 | // To tolerate 2 failures, IBFT requires 7 nodes 20 | // 7 = 3 * 2 + 1 21 | // It should always take the floor of the result 22 | return (s.Len() - 1) / 3 23 | } 24 | 25 | type QuorumImplementation func(validators.Validators) int 26 | 27 | // LegacyQuorumSize returns the legacy quorum size for the given validator set 28 | func LegacyQuorumSize(set validators.Validators) int { 29 | // According to the IBFT spec, the number of valid messages 30 | // needs to be 2F + 1 31 | return 2*CalcMaxFaultyNodes(set) + 1 32 | } 33 | 34 | // OptimalQuorumSize returns the optimal quorum size for the given validator set 35 | func OptimalQuorumSize(set validators.Validators) int { 36 | // if the number of validators is less than 4, 37 | // then the entire set is required 38 | if CalcMaxFaultyNodes(set) == 0 { 39 | /* 40 | N: 1 -> Q: 1 41 | N: 2 -> Q: 2 42 | N: 3 -> Q: 3 43 | */ 44 | return set.Len() 45 | } 46 | 47 | // (quorum optimal) Q = ceil(2/3 * N) 48 | return int(math.Ceil(2 * float64(set.Len()) / 3)) 49 | } 50 | 51 | func CalcProposer( 52 | validators validators.Validators, 53 | round uint64, 54 | lastProposer types.Address, 55 | ) validators.Validator { 56 | var seed uint64 57 | 58 | if lastProposer == types.ZeroAddress { 59 | seed = round 60 | } else { 61 | offset := int64(0) 62 | 63 | if index := validators.Index(lastProposer); index != -1 { 64 | offset = index 65 | } 66 | 67 | seed = uint64(offset) + round + 1 68 | } 69 | 70 | pick := seed % uint64(validators.Len()) 71 | 72 | return validators.At(pick) 73 | } 74 | -------------------------------------------------------------------------------- /consensus/utils.go: -------------------------------------------------------------------------------- 1 | package consensus 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/types" 5 | "github.com/ExzoNetwork/ExzoCoin/types/buildroot" 6 | ) 7 | 8 | // BuildBlockParams are parameters passed into the BuildBlock helper method 9 | type BuildBlockParams struct { 10 | Header *types.Header 11 | Txns []*types.Transaction 12 | Receipts []*types.Receipt 13 | } 14 | 15 | // BuildBlock is a utility function that builds a block, based on the passed in header, transactions and receipts 16 | func BuildBlock(params BuildBlockParams) *types.Block { 17 | txs := params.Txns 18 | header := params.Header 19 | 20 | if len(txs) == 0 { 21 | header.TxRoot = types.EmptyRootHash 22 | } else { 23 | header.TxRoot = buildroot.CalculateTransactionsRoot(txs) 24 | } 25 | 26 | if len(params.Receipts) == 0 { 27 | header.ReceiptsRoot = types.EmptyRootHash 28 | } else { 29 | header.ReceiptsRoot = buildroot.CalculateReceiptsRoot(params.Receipts) 30 | } 31 | 32 | // TODO: Compute uncles 33 | header.Sha3Uncles = types.EmptyUncleHash 34 | header.ComputeHash() 35 | 36 | return &types.Block{ 37 | Header: header, 38 | Transactions: txs, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/abis/abis.go: -------------------------------------------------------------------------------- 1 | package abis 2 | 3 | import ( 4 | "github.com/umbracle/ethgo/abi" 5 | ) 6 | 7 | var ( 8 | // ABI for Staking Contract 9 | StakingABI = abi.MustNewABI(StakingJSONABI) 10 | 11 | // ABI for Contract used in e2e stress test 12 | StressTestABI = abi.MustNewABI(StressTestJSONABI) 13 | ) 14 | -------------------------------------------------------------------------------- /docker/local/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.18-alpine AS builder 2 | 3 | WORKDIR /exzocoin 4 | 5 | ADD go.mod go.sum ./ 6 | RUN go mod download 7 | 8 | COPY . . 9 | 10 | RUN go build -o exzocoin main.go 11 | 12 | FROM alpine:latest AS runner 13 | 14 | RUN apk --no-cache add ca-certificates jq 15 | 16 | WORKDIR /exzocoin 17 | 18 | COPY --from=builder /exzocoin/exzocoin ./ 19 | COPY ./docker/local/exzocoin.sh ./ 20 | 21 | # Expose json-rpc, libp2p and grpc ports 22 | EXPOSE 8545 9632 1478 5001 23 | 24 | ENTRYPOINT ["./exzocoin.sh"] 25 | -------------------------------------------------------------------------------- /docker/local/exzocoin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | POLYGON_EDGE_BIN=./exzocoin 6 | GENESIS_PATH=/genesis/genesis.json 7 | 8 | case "$1" in 9 | 10 | "init") 11 | if [ -f "$GENESIS_PATH" ]; then 12 | echo "Secrets have already been generated." 13 | else 14 | echo "Generating secrets..." 15 | secrets=$("$POLYGON_EDGE_BIN" secrets init --num 4 --data-dir data- --json) 16 | echo "Secrets have been successfully generated" 17 | 18 | echo "Generating genesis file..." 19 | "$POLYGON_EDGE_BIN" genesis \ 20 | --dir "$GENESIS_PATH" \ 21 | --consensus ibft \ 22 | --ibft-validators-prefix-path data- \ 23 | --bootnode /dns4/node-1/tcp/1478/p2p/$(echo $secrets | jq -r '.[0] | .node_id') \ 24 | --bootnode /dns4/node-2/tcp/1478/p2p/$(echo $secrets | jq -r '.[1] | .node_id') 25 | echo "Genesis file has been successfully generated" 26 | fi 27 | ;; 28 | 29 | *) 30 | until [ -f "$GENESIS_PATH" ] 31 | do 32 | echo "Waiting 1s for genesis file $GENESIS_PATH to be created by init container..." 33 | sleep 1 34 | done 35 | echo "Executing exzocoin..." 36 | exec "$POLYGON_EDGE_BIN" "$@" 37 | ;; 38 | 39 | esac 40 | -------------------------------------------------------------------------------- /e2e/README.md: -------------------------------------------------------------------------------- 1 | # E2E tests 2 | 3 | The implemented E2E tests start a local instance of exzocoin. 4 | 5 | ## Step 1: Run the tests 6 | 7 | Use the make file to launch the tests `make test-e2e` 8 | 9 | ## Manual checks if things are acting funny 10 | 11 | ### Check if the exzocoin process is running 12 | 13 | If you've stopped the tests abruptly, chances are the exzocoin process is still running on your machine.
14 | In order for the tests to function normally, please kill the possible remaining processes using `killall exzocoin` 15 | 16 | ### Clean the golang test cache 17 | 18 | Golang caches test results, and may not even run a test, causing tests to fail on some machines and work on others. 19 | ````bash 20 | go clean -testcache 21 | ```` 22 | 23 | This command cleans the test cache, and should be added to the runtime config. 24 | 25 | Another way to disable test caching altogether is to add the following flag when running go test `-count=1`: 26 | ````bash 27 | go test ./... -count=1 28 | ```` 29 | 30 | ## Note 31 | 32 | ### constants 33 | 34 | Constant values that used in some e2e tests are defined in `e2e/const.go`. 35 | Mock contract are pre-compiled and the result is stored in `const.go` in order to avoid dependencies of solc command (solidity compiler). 36 | 37 | You can get the byte code from original program by following command. 38 | 39 | ```shell 40 | $ solc --bin e2e/sample.sol 41 | ``` 42 | 43 | Currently you need to build with version 0.5.x compiler. You can check the compiler version by `solc --version`. 44 | 45 | ```shell 46 | $ solc --version 47 | solc, the solidity compiler commandline interface 48 | Version: 0.5.17+commit.d19bba13.Darwin.appleclang 49 | ``` 50 | -------------------------------------------------------------------------------- /e2e/metadata/StressTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | contract StressTest { 4 | string private name; 5 | uint256 private num; 6 | 7 | constructor (){ 8 | num = 0; 9 | } 10 | 11 | event txnDone(uint number); 12 | 13 | function setName(string memory sName) external { 14 | num++; 15 | name = sName; 16 | emit txnDone(num); 17 | } 18 | 19 | function getCount() view external returns (uint){ 20 | return num; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /e2e/metadata/sample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.5; 2 | pragma experimental ABIEncoderV2; 3 | 4 | contract Sample { 5 | event A(address indexed val_0, address indexed val_1); 6 | 7 | function setterA(address val_0, address val_1) public payable { 8 | emit A(val_0, val_1); 9 | } 10 | 11 | function setA1() public payable { 12 | emit A( 13 | 0x0000000000000000000000000000000000000000, 14 | 0x0100000000000000000000000000000000000000 15 | ); 16 | } 17 | 18 | function setA2() public payable { 19 | emit A( 20 | 0x0100000000000000000000000000000000000000, 21 | 0x0000000000000000000000000000000000000000 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /e2e/syncer_test.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | "time" 8 | 9 | "github.com/ExzoNetwork/ExzoCoin/e2e/framework" 10 | "github.com/ExzoNetwork/ExzoCoin/validators" 11 | ) 12 | 13 | func TestClusterBlockSync(t *testing.T) { 14 | const ( 15 | numNonValidators = 2 16 | desiredHeight = 10 17 | ) 18 | 19 | runTest := func(t *testing.T, validatorType validators.ValidatorType) { 20 | t.Helper() 21 | 22 | // Start IBFT cluster (4 Validator + 2 Non-Validator) 23 | ibftManager := framework.NewIBFTServersManager( 24 | t, 25 | IBFTMinNodes+numNonValidators, 26 | IBFTDirPrefix, func(i int, config *framework.TestServerConfig) { 27 | config.SetValidatorType(validatorType) 28 | 29 | if i >= IBFTMinNodes { 30 | // Other nodes should not be in the validator set 31 | dirPrefix := "exzocoin-non-validator-" 32 | config.SetIBFTDirPrefix(dirPrefix) 33 | config.SetIBFTDir(fmt.Sprintf("%s%d", dirPrefix, i)) 34 | } 35 | }) 36 | 37 | startContext, startCancelFn := context.WithTimeout(context.Background(), time.Minute) 38 | defer startCancelFn() 39 | ibftManager.StartServers(startContext) 40 | 41 | servers := make([]*framework.TestServer, 0) 42 | for i := 0; i < IBFTMinNodes+numNonValidators; i++ { 43 | servers = append(servers, ibftManager.GetServer(i)) 44 | } 45 | // All nodes should have mined the same block eventually 46 | waitErrors := framework.WaitForServersToSeal(servers, desiredHeight) 47 | 48 | if len(waitErrors) != 0 { 49 | t.Fatalf("Unable to wait for all nodes to seal blocks, %v", waitErrors) 50 | } 51 | } 52 | 53 | t.Run("ECDSA", func(t *testing.T) { 54 | runTest(t, validators.ECDSAValidatorType) 55 | }) 56 | 57 | t.Run("BLS", func(t *testing.T) { 58 | runTest(t, validators.BLSValidatorType) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /helper/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/chain" 5 | "github.com/ExzoNetwork/ExzoCoin/types" 6 | ) 7 | 8 | // GetWhitelist fetches whitelist object from the config 9 | func GetWhitelist(config *chain.Chain) *chain.Whitelists { 10 | return config.Params.Whitelists 11 | } 12 | 13 | // GetDeploymentWhitelist fetches deployment whitelist from the genesis config 14 | // if doesn't exist returns empty list 15 | func GetDeploymentWhitelist(genesisConfig *chain.Chain) ([]types.Address, error) { 16 | // Fetch whitelist config if exists, if not init 17 | whitelistConfig := GetWhitelist(genesisConfig) 18 | 19 | // Extract deployment whitelist if exists, if not init 20 | if whitelistConfig == nil { 21 | return make([]types.Address, 0), nil 22 | } 23 | 24 | return whitelistConfig.Deployment, nil 25 | } 26 | -------------------------------------------------------------------------------- /helper/enode/enode_test.go: -------------------------------------------------------------------------------- 1 | package enode 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestParseEnode(t *testing.T) { 9 | id1 := "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439" 10 | 11 | enode := func(prefix, id, ip, port string) string { 12 | return fmt.Sprintf("%s://%s@%s:%s", prefix, id, ip, port) 13 | } 14 | 15 | cases := []struct { 16 | Name string 17 | enode string 18 | err bool 19 | }{ 20 | { 21 | Name: "Incorrect scheme", 22 | enode: "foo://1234", 23 | err: true, 24 | }, 25 | { 26 | Name: "Incorrect IP", 27 | enode: enode("enode", id1, "abc", "30303"), 28 | err: true, 29 | }, 30 | { 31 | Name: "IP too long", 32 | enode: enode("enode", id1, "127.0.0.1.1", "30303"), 33 | err: true, 34 | }, 35 | { 36 | Name: "IP too short", 37 | enode: enode("enode", id1, "127.0.0", "30303"), 38 | err: true, 39 | }, 40 | { 41 | Name: "ID with 0x prefix", 42 | enode: enode("enode", "0x"+id1, "127.0.0.1.1", "30303"), 43 | err: true, 44 | }, 45 | { 46 | Name: "ID is not hex", 47 | enode: enode("enode", "abcd", "127.0.0.1", "30303"), 48 | err: true, 49 | }, 50 | { 51 | Name: "ID incorrect size", 52 | enode: enode("enode", id1[0:10], "127.0.0.1", "30303"), 53 | err: true, 54 | }, 55 | { 56 | Name: "Port is not a number", 57 | enode: enode("enode", id1, "127.0.0.1", "aa"), 58 | err: true, 59 | }, 60 | { 61 | Name: "Valid enode", 62 | enode: enode("enode", id1, "127.0.0.1", "30303"), 63 | }, 64 | } 65 | 66 | for _, c := range cases { 67 | t.Run(c.Name, func(t *testing.T) { 68 | node, err := ParseURL(c.enode) 69 | if c.err && err == nil { 70 | t.Fatal("expected error") 71 | } else if !c.err && err != nil { 72 | t.Fatal("error not expected") 73 | } 74 | 75 | if err == nil { 76 | if node.String() != c.enode { 77 | t.Fatalf("bad") 78 | } 79 | } 80 | }) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /helper/hex/hex_test.go: -------------------------------------------------------------------------------- 1 | package hex 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | // TestDecodeUint64 verifies that uint64 values 11 | // are properly decoded from hex 12 | func TestDecodeUint64(t *testing.T) { 13 | t.Parallel() 14 | 15 | uint64Array := []uint64{ 16 | 0, 17 | 1, 18 | 11, 19 | 67312, 20 | 80604, 21 | ^uint64(0), // max uint64 22 | } 23 | 24 | toHexArr := func(nums []uint64) []string { 25 | numbers := make([]string, len(nums)) 26 | 27 | for index, num := range nums { 28 | numbers[index] = fmt.Sprintf("0x%x", num) 29 | } 30 | 31 | return numbers 32 | } 33 | 34 | for index, value := range toHexArr(uint64Array) { 35 | decodedValue, err := DecodeUint64(value) 36 | assert.NoError(t, err) 37 | 38 | assert.Equal(t, uint64Array[index], decodedValue) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /helper/ipc/ipc_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package ipc 5 | 6 | import ( 7 | "net" 8 | "os" 9 | "path/filepath" 10 | "time" 11 | ) 12 | 13 | // Dial dials an IPC path 14 | func Dial(path string) (net.Conn, error) { 15 | return net.Dial("unix", path) 16 | } 17 | 18 | // DialTimeout dials an IPC path with timeout 19 | func DialTimeout(path string, timeout time.Duration) (net.Conn, error) { 20 | return net.DialTimeout("unix", path, timeout) 21 | } 22 | 23 | // Listen listens an IPC path 24 | func Listen(path string) (net.Listener, error) { 25 | if err := os.MkdirAll(filepath.Dir(path), 0751); err != nil { 26 | return nil, err 27 | } 28 | 29 | if removeErr := os.Remove(path); removeErr != nil { 30 | return nil, removeErr 31 | } 32 | 33 | lis, err := net.Listen("unix", path) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | if chmodErr := os.Chmod(path, 0600); chmodErr != nil { 39 | return nil, chmodErr 40 | } 41 | 42 | return lis, nil 43 | } 44 | -------------------------------------------------------------------------------- /helper/ipc/ipc_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package ipc 5 | 6 | import ( 7 | "net" 8 | "time" 9 | 10 | "gopkg.in/natefinch/npipe.v2" 11 | ) 12 | 13 | // Dial dials an IPC path 14 | func Dial(path string) (net.Conn, error) { 15 | return npipe.Dial(path) 16 | } 17 | 18 | // DialTimeout dials an IPC path with timeout 19 | func DialTimeout(path string, timeout time.Duration) (net.Conn, error) { 20 | return npipe.DialTimeout(path, timeout) 21 | } 22 | 23 | // Listen listens an IPC path 24 | func Listen(path string) (net.Listener, error) { 25 | return npipe.Listen(path) 26 | } 27 | -------------------------------------------------------------------------------- /helper/keccak/keccak.go: -------------------------------------------------------------------------------- 1 | package keccak 2 | 3 | import ( 4 | "hash" 5 | 6 | "github.com/umbracle/fastrlp" 7 | "golang.org/x/crypto/sha3" 8 | ) 9 | 10 | type hashImpl interface { 11 | hash.Hash 12 | Read(b []byte) (int, error) 13 | } 14 | 15 | // Keccak is the sha256 keccak hash 16 | type Keccak struct { 17 | buf []byte // buffer to store intermediate rlp marshal values 18 | tmp []byte 19 | hash hashImpl 20 | } 21 | 22 | // WriteRlp writes an RLP value 23 | func (k *Keccak) WriteRlp(dst []byte, v *fastrlp.Value) []byte { 24 | k.buf = v.MarshalTo(k.buf[:0]) 25 | k.Write(k.buf) 26 | 27 | return k.Sum(dst) 28 | } 29 | 30 | // Write implements the hash interface 31 | func (k *Keccak) Write(b []byte) (int, error) { 32 | return k.hash.Write(b) 33 | } 34 | 35 | // Reset implements the hash interface 36 | func (k *Keccak) Reset() { 37 | k.buf = k.buf[:0] 38 | k.hash.Reset() 39 | } 40 | 41 | // Read hashes the content and returns the intermediate buffer. 42 | func (k *Keccak) Read() []byte { 43 | k.hash.Read(k.tmp) 44 | 45 | return k.tmp 46 | } 47 | 48 | // Sum implements the hash interface 49 | func (k *Keccak) Sum(dst []byte) []byte { 50 | k.hash.Read(k.tmp) 51 | dst = append(dst, k.tmp[:]...) 52 | 53 | return dst 54 | } 55 | 56 | func newKeccak(hash hashImpl) *Keccak { 57 | return &Keccak{ 58 | hash: hash, 59 | tmp: make([]byte, hash.Size()), 60 | } 61 | } 62 | 63 | // NewKeccak256 returns a new keccak 256 64 | func NewKeccak256() *Keccak { 65 | impl, ok := sha3.NewLegacyKeccak256().(hashImpl) 66 | if !ok { 67 | return nil 68 | } 69 | 70 | return newKeccak(impl) 71 | } 72 | -------------------------------------------------------------------------------- /helper/keccak/keccak_test.go: -------------------------------------------------------------------------------- 1 | package keccak 2 | -------------------------------------------------------------------------------- /helper/keccak/pool.go: -------------------------------------------------------------------------------- 1 | package keccak 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/umbracle/fastrlp" 7 | ) 8 | 9 | // DefaultKeccakPool is a default pool 10 | var DefaultKeccakPool Pool 11 | 12 | // Pool is a pool of keccaks 13 | type Pool struct { 14 | pool sync.Pool 15 | } 16 | 17 | // Get returns the keccak 18 | func (p *Pool) Get() *Keccak { 19 | v := p.pool.Get() 20 | if v == nil { 21 | return NewKeccak256() 22 | } 23 | 24 | keccakVal, ok := v.(*Keccak) 25 | if !ok { 26 | return nil 27 | } 28 | 29 | return keccakVal 30 | } 31 | 32 | // Put releases the keccak 33 | func (p *Pool) Put(k *Keccak) { 34 | k.Reset() 35 | p.pool.Put(k) 36 | } 37 | 38 | // Keccak256 hashes a src with keccak-256 39 | func Keccak256(dst, src []byte) []byte { 40 | h := DefaultKeccakPool.Get() 41 | h.Write(src) 42 | dst = h.Sum(dst) 43 | DefaultKeccakPool.Put(h) 44 | 45 | return dst 46 | } 47 | 48 | // Keccak256Rlp hashes a fastrlp.Value with keccak-256 49 | func Keccak256Rlp(dst []byte, src *fastrlp.Value) []byte { 50 | h := DefaultKeccakPool.Get() 51 | dst = h.WriteRlp(dst, src) 52 | DefaultKeccakPool.Put(h) 53 | 54 | return dst 55 | } 56 | -------------------------------------------------------------------------------- /helper/keystore/keystore.go: -------------------------------------------------------------------------------- 1 | package keystore 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type createFn func() ([]byte, error) 10 | 11 | // CreateIfNotExists generates a private key at the specified path, 12 | // or reads the file on that path if it is present 13 | func CreateIfNotExists(path string, create createFn) ([]byte, error) { 14 | _, err := os.Stat(path) 15 | if err != nil && !os.IsNotExist(err) { 16 | return nil, fmt.Errorf("failed to stat (%s): %w", path, err) 17 | } 18 | 19 | var keyBuff []byte 20 | if !os.IsNotExist(err) { 21 | // Key exists 22 | keyBuff, err = os.ReadFile(path) 23 | if err != nil { 24 | return nil, fmt.Errorf("unable to read private key from disk (%s), %w", path, err) 25 | } 26 | 27 | return keyBuff, nil 28 | } 29 | 30 | // Key doesn't exist yet, generate it 31 | keyBuff, err = create() 32 | if err != nil { 33 | return nil, fmt.Errorf("unable to generate private key, %w", err) 34 | } 35 | 36 | // Encode it to a readable format (Base64) and write to disk 37 | keyBuff = []byte(hex.EncodeToString(keyBuff)) 38 | if err = os.WriteFile(path, keyBuff, os.ModePerm); err != nil { 39 | return nil, fmt.Errorf("unable to write private key to disk (%s), %w", path, err) 40 | } 41 | 42 | return keyBuff, nil 43 | } 44 | 45 | func CreatePrivateKey(create createFn) ([]byte, error) { 46 | keyBuff, err := create() 47 | if err != nil { 48 | return nil, fmt.Errorf("unable to generate private key, %w", err) 49 | } 50 | 51 | // Encode it to a readable format (Base64) and return 52 | return []byte(hex.EncodeToString(keyBuff)), nil 53 | } 54 | -------------------------------------------------------------------------------- /helper/tests/assert.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | // AssertErrorMessageContains is a helper function to make sure 10 | // an error message matches with the expected error which is not exported 11 | // if expected error is nil, it checks the target error is nil 12 | func AssertErrorMessageContains(t *testing.T, expected, target error) { 13 | t.Helper() 14 | 15 | if expected != nil { 16 | assert.ErrorContains(t, target, expected.Error()) 17 | } else { 18 | assert.NoError(t, target) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /jsonrpc/eth_txpool_test.go: -------------------------------------------------------------------------------- 1 | package jsonrpc 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/types" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestEth_TxnPool_SendRawTransaction(t *testing.T) { 12 | store := &mockStoreTxn{} 13 | eth := newTestEthEndpoint(store) 14 | 15 | txn := &types.Transaction{ 16 | From: addr0, 17 | V: big.NewInt(1), 18 | } 19 | txn.ComputeHash() 20 | 21 | data := txn.MarshalRLP() 22 | _, err := eth.SendRawTransaction(data) 23 | assert.NoError(t, err) 24 | assert.NotEqual(t, store.txn.Hash, types.ZeroHash) 25 | 26 | // the hash in the txn pool should match the one we send 27 | if txn.Hash != store.txn.Hash { 28 | t.Fatal("bad") 29 | } 30 | } 31 | 32 | func TestEth_TxnPool_SendTransaction(t *testing.T) { 33 | store := &mockStoreTxn{} 34 | store.AddAccount(addr0) 35 | eth := newTestEthEndpoint(store) 36 | 37 | txToSend := &types.Transaction{ 38 | From: addr0, 39 | To: argAddrPtr(addr0), 40 | Nonce: uint64(0), 41 | GasPrice: big.NewInt(int64(1)), 42 | } 43 | 44 | _, err := eth.SendRawTransaction(txToSend.MarshalRLP()) 45 | assert.NoError(t, err) 46 | assert.NotEqual(t, store.txn.Hash, types.ZeroHash) 47 | } 48 | 49 | type mockStoreTxn struct { 50 | ethStore 51 | accounts map[types.Address]*mockAccount 52 | txn *types.Transaction 53 | } 54 | 55 | func (m *mockStoreTxn) AddTx(tx *types.Transaction) error { 56 | m.txn = tx 57 | 58 | return nil 59 | } 60 | 61 | func (m *mockStoreTxn) GetNonce(addr types.Address) uint64 { 62 | return 1 63 | } 64 | 65 | func (m *mockStoreTxn) AddAccount(addr types.Address) *mockAccount { 66 | if m.accounts == nil { 67 | m.accounts = map[types.Address]*mockAccount{} 68 | } 69 | 70 | acct := &mockAccount{ 71 | address: addr, 72 | account: &Account{}, 73 | storage: make(map[types.Hash][]byte), 74 | } 75 | m.accounts[addr] = acct 76 | 77 | return acct 78 | } 79 | 80 | func (m *mockStoreTxn) Header() *types.Header { 81 | return &types.Header{} 82 | } 83 | 84 | func (m *mockStoreTxn) GetAccount(root types.Hash, addr types.Address) (*Account, error) { 85 | acct, ok := m.accounts[addr] 86 | if !ok { 87 | return nil, ErrStateNotFound 88 | } 89 | 90 | return acct.account, nil 91 | } 92 | -------------------------------------------------------------------------------- /jsonrpc/jsonrpc_test.go: -------------------------------------------------------------------------------- 1 | package jsonrpc 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "net" 7 | "testing" 8 | 9 | "github.com/ExzoNetwork/ExzoCoin/helper/tests" 10 | "github.com/ExzoNetwork/ExzoCoin/versioning" 11 | "github.com/stretchr/testify/assert" 12 | 13 | "github.com/hashicorp/go-hclog" 14 | ) 15 | 16 | func TestHTTPServer(t *testing.T) { 17 | store := newMockStore() 18 | port, portErr := tests.GetFreePort() 19 | 20 | if portErr != nil { 21 | t.Fatalf("Unable to fetch free port, %v", portErr) 22 | } 23 | 24 | config := &Config{ 25 | Store: store, 26 | Addr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: port}, 27 | } 28 | _, err := NewJSONRPC(hclog.NewNullLogger(), config) 29 | 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | } 34 | 35 | func Test_handleGetRequest(t *testing.T) { 36 | var ( 37 | chainName = "exzocoin-test" 38 | chainID = uint64(200) 39 | ) 40 | 41 | jsonRPC := &JSONRPC{ 42 | config: &Config{ 43 | ChainName: chainName, 44 | ChainID: chainID, 45 | }, 46 | } 47 | 48 | mockWriter := bytes.NewBuffer(nil) 49 | 50 | jsonRPC.handleGetRequest(mockWriter) 51 | 52 | response := &GetResponse{} 53 | 54 | assert.NoError( 55 | t, 56 | json.Unmarshal(mockWriter.Bytes(), response), 57 | ) 58 | 59 | assert.Equal( 60 | t, 61 | &GetResponse{ 62 | Name: chainName, 63 | ChainID: chainID, 64 | Version: versioning.Version, 65 | }, 66 | response, 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /jsonrpc/net_endpoint.go: -------------------------------------------------------------------------------- 1 | package jsonrpc 2 | 3 | import "strconv" 4 | 5 | // networkStore provides methods needed for Net endpoint 6 | type networkStore interface { 7 | GetPeers() int 8 | } 9 | 10 | // Net is the net jsonrpc endpoint 11 | type Net struct { 12 | store networkStore 13 | chainID uint64 14 | } 15 | 16 | // Version returns the current network id 17 | func (n *Net) Version() (interface{}, error) { 18 | return strconv.FormatUint(n.chainID, 10), nil 19 | } 20 | 21 | // Listening returns true if client is actively listening for network connections 22 | func (n *Net) Listening() (interface{}, error) { 23 | return true, nil 24 | } 25 | 26 | // PeerCount returns number of peers currently connected to the client 27 | func (n *Net) PeerCount() (interface{}, error) { 28 | peers := n.store.GetPeers() 29 | 30 | return argUint64(peers), nil 31 | } 32 | -------------------------------------------------------------------------------- /jsonrpc/net_endpoint_test.go: -------------------------------------------------------------------------------- 1 | package jsonrpc 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/go-hclog" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNetEndpoint_PeerCount(t *testing.T) { 11 | dispatcher := newDispatcher( 12 | hclog.NewNullLogger(), 13 | newMockStore(), 14 | &dispatcherParams{ 15 | chainID: 1, 16 | }) 17 | 18 | resp, err := dispatcher.Handle([]byte(`{ 19 | "method": "net_peerCount", 20 | "params": [""] 21 | }`)) 22 | assert.NoError(t, err) 23 | 24 | var res string 25 | 26 | assert.NoError(t, expectJSONResult(resp, &res)) 27 | assert.Equal(t, "0x14", res) 28 | } 29 | -------------------------------------------------------------------------------- /jsonrpc/testsuite/block-empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "parentHash": "0x0100000000000000000000000000000000000000000000000000000000000000", 3 | "sha3Uncles": "0x0200000000000000000000000000000000000000000000000000000000000000", 4 | "miner": "0x0100000000000000000000000000000000000000", 5 | "stateRoot": "0x0400000000000000000000000000000000000000000000000000000000000000", 6 | "transactionsRoot": "0x0500000000000000000000000000000000000000000000000000000000000000", 7 | "receiptsRoot": "0x0600000000000000000000000000000000000000000000000000000000000000", 8 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 9 | "difficulty": "0xa", 10 | "totalDifficulty": "0x0", 11 | "size": "0x0", 12 | "number": "0xb", 13 | "gasLimit": "0xc", 14 | "gasUsed": "0xd", 15 | "timestamp": "0xe", 16 | "extraData": "0x616263646566", 17 | "mixHash": "0x0700000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0a00000000000000", 19 | "hash": "0x0800000000000000000000000000000000000000000000000000000000000000", 20 | "transactions": null, 21 | "uncles": null 22 | } -------------------------------------------------------------------------------- /jsonrpc/testsuite/block-with-txn-bodies.json: -------------------------------------------------------------------------------- 1 | { 2 | "parentHash": "0x0100000000000000000000000000000000000000000000000000000000000000", 3 | "sha3Uncles": "0x0200000000000000000000000000000000000000000000000000000000000000", 4 | "miner": "0x0100000000000000000000000000000000000000", 5 | "stateRoot": "0x0400000000000000000000000000000000000000000000000000000000000000", 6 | "transactionsRoot": "0x0500000000000000000000000000000000000000000000000000000000000000", 7 | "receiptsRoot": "0x0600000000000000000000000000000000000000000000000000000000000000", 8 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 9 | "difficulty": "0xa", 10 | "totalDifficulty": "0x0", 11 | "size": "0x0", 12 | "number": "0xb", 13 | "gasLimit": "0xc", 14 | "gasUsed": "0xd", 15 | "timestamp": "0xe", 16 | "extraData": "0x616263646566", 17 | "mixHash": "0x0700000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0a00000000000000", 19 | "hash": "0x0800000000000000000000000000000000000000000000000000000000000000", 20 | "transactions": [ 21 | { 22 | "nonce": "0x1", 23 | "gasPrice": "0xa", 24 | "gas": "0x64", 25 | "to": "0x0000000000000000000000000000000000000000", 26 | "value": "0x3e8", 27 | "input": "0x0102", 28 | "v": "0x1", 29 | "r": "0x2", 30 | "s": "0x3", 31 | "hash": "0x0200000000000000000000000000000000000000000000000000000000000000", 32 | "from": "0x0300000000000000000000000000000000000000", 33 | "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 34 | "blockNumber": "0x1", 35 | "transactionIndex": "0x2" 36 | } 37 | ], 38 | "uncles": null 39 | } -------------------------------------------------------------------------------- /jsonrpc/testsuite/block-with-txn-hashes.json: -------------------------------------------------------------------------------- 1 | { 2 | "parentHash": "0x0100000000000000000000000000000000000000000000000000000000000000", 3 | "sha3Uncles": "0x0200000000000000000000000000000000000000000000000000000000000000", 4 | "miner": "0x0100000000000000000000000000000000000000", 5 | "stateRoot": "0x0400000000000000000000000000000000000000000000000000000000000000", 6 | "transactionsRoot": "0x0500000000000000000000000000000000000000000000000000000000000000", 7 | "receiptsRoot": "0x0600000000000000000000000000000000000000000000000000000000000000", 8 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 9 | "difficulty": "0xa", 10 | "totalDifficulty": "0x0", 11 | "size": "0x0", 12 | "number": "0xb", 13 | "gasLimit": "0xc", 14 | "gasUsed": "0xd", 15 | "timestamp": "0xe", 16 | "extraData": "0x616263646566", 17 | "mixHash": "0x0700000000000000000000000000000000000000000000000000000000000000", 18 | "nonce": "0x0a00000000000000", 19 | "hash": "0x0800000000000000000000000000000000000000000000000000000000000000", 20 | "transactions": [ 21 | "0x0800000000000000000000000000000000000000000000000000000000000000" 22 | ], 23 | "uncles": null 24 | } -------------------------------------------------------------------------------- /jsonrpc/testsuite/transaction-pending.json: -------------------------------------------------------------------------------- 1 | { 2 | "nonce": "0x1", 3 | "gasPrice": "0xa", 4 | "gas": "0x64", 5 | "to": "0x0000000000000000000000000000000000000000", 6 | "value": "0x3e8", 7 | "input": "0x0102", 8 | "v": "0x1", 9 | "r": "0x2", 10 | "s": "0x3", 11 | "hash": "0x0200000000000000000000000000000000000000000000000000000000000000", 12 | "from": "0x0300000000000000000000000000000000000000", 13 | "blockHash": null, 14 | "blockNumber": null, 15 | "transactionIndex": null 16 | } -------------------------------------------------------------------------------- /jsonrpc/testsuite/transaction-sealed.json: -------------------------------------------------------------------------------- 1 | { 2 | "nonce": "0x1", 3 | "gasPrice": "0xa", 4 | "gas": "0x64", 5 | "to": "0x0000000000000000000000000000000000000000", 6 | "value": "0x3e8", 7 | "input": "0x0102", 8 | "v": "0x1", 9 | "r": "0x2", 10 | "s": "0x3", 11 | "hash": "0x0200000000000000000000000000000000000000000000000000000000000000", 12 | "from": "0x0300000000000000000000000000000000000000", 13 | "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 14 | "blockNumber": "0x1", 15 | "transactionIndex": "0x2" 16 | } -------------------------------------------------------------------------------- /jsonrpc/web3_endpoint.go: -------------------------------------------------------------------------------- 1 | package jsonrpc 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/helper/keccak" 7 | "github.com/ExzoNetwork/ExzoCoin/versioning" 8 | ) 9 | 10 | // Web3 is the web3 jsonrpc endpoint 11 | type Web3 struct { 12 | chainID uint64 13 | chainName string 14 | } 15 | 16 | var clientVersionTemplate = "%s [chain-id: %d] [version: %s]" 17 | 18 | // ClientVersion returns the version of the web3 client (web3_clientVersion) 19 | func (w *Web3) ClientVersion() (interface{}, error) { 20 | return fmt.Sprintf( 21 | clientVersionTemplate, 22 | w.chainName, 23 | w.chainID, 24 | versioning.Version, 25 | ), nil 26 | } 27 | 28 | // Sha3 returns Keccak-256 (not the standardized SHA3-256) of the given data 29 | func (w *Web3) Sha3(v argBytes) (interface{}, error) { 30 | dst := keccak.Keccak256(nil, v) 31 | 32 | return argBytes(dst), nil 33 | } 34 | -------------------------------------------------------------------------------- /jsonrpc/web3_endpoint_test.go: -------------------------------------------------------------------------------- 1 | package jsonrpc 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/versioning" 8 | 9 | "github.com/hashicorp/go-hclog" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestWeb3EndpointSha3(t *testing.T) { 14 | dispatcher := newDispatcher( 15 | hclog.NewNullLogger(), 16 | newMockStore(), 17 | &dispatcherParams{ 18 | chainID: 0, 19 | priceLimit: 0, 20 | jsonRPCBatchLengthLimit: 20, 21 | blockRangeLimit: 1000, 22 | }) 23 | 24 | resp, err := dispatcher.Handle([]byte(`{ 25 | "method": "web3_sha3", 26 | "params": ["0x68656c6c6f20776f726c64"] 27 | }`)) 28 | assert.NoError(t, err) 29 | 30 | var res string 31 | 32 | assert.NoError(t, expectJSONResult(resp, &res)) 33 | assert.Equal(t, "0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad", res) 34 | } 35 | 36 | func TestWeb3EndpointClientVersion(t *testing.T) { 37 | var ( 38 | chainName = "test-chain" 39 | chainID = uint64(100) 40 | ) 41 | 42 | dispatcher := newDispatcher( 43 | hclog.NewNullLogger(), 44 | newMockStore(), 45 | &dispatcherParams{ 46 | chainID: chainID, 47 | chainName: chainName, 48 | priceLimit: 0, 49 | jsonRPCBatchLengthLimit: 20, 50 | blockRangeLimit: 1000, 51 | }, 52 | ) 53 | 54 | resp, err := dispatcher.Handle([]byte(`{ 55 | "method": "web3_clientVersion", 56 | "params": [] 57 | }`)) 58 | assert.NoError(t, err) 59 | 60 | var res string 61 | 62 | assert.NoError(t, expectJSONResult(resp, &res)) 63 | assert.Contains(t, res, 64 | fmt.Sprintf( 65 | clientVersionTemplate, 66 | chainName, 67 | chainID, 68 | versioning.Version, 69 | ), 70 | ) 71 | } 72 | -------------------------------------------------------------------------------- /licenses/licenses.go: -------------------------------------------------------------------------------- 1 | package licenses 2 | 3 | import ( 4 | _ "embed" 5 | 6 | "encoding/json" 7 | ) 8 | 9 | type DepLicense struct { 10 | Name string `json:"name"` 11 | Version *string `json:"version"` 12 | Type string `json:"type"` 13 | Path string `json:"path"` 14 | } 15 | 16 | var ( 17 | // Polygon Edge License 18 | License string 19 | 20 | // Dependency Licenses 21 | //go:embed bsd_licenses.json 22 | bsdLicensesJSON string 23 | bsdLicenses []DepLicense 24 | ) 25 | 26 | func SetLicense(license string) { 27 | License = license 28 | } 29 | 30 | func GetBSDLicenses() ([]DepLicense, error) { 31 | if bsdLicenses == nil { 32 | if err := json.Unmarshal([]byte(bsdLicensesJSON), &bsdLicenses); err != nil { 33 | return nil, err 34 | } 35 | } 36 | 37 | return bsdLicenses, nil 38 | } 39 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/command/root" 7 | "github.com/ExzoNetwork/ExzoCoin/licenses" 8 | ) 9 | 10 | var ( 11 | //go:embed LICENSE 12 | license string 13 | ) 14 | 15 | func main() { 16 | licenses.SetLicense(license) 17 | 18 | root.NewRootCommand().Execute() 19 | } 20 | -------------------------------------------------------------------------------- /network/bootnodes.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "sync/atomic" 5 | 6 | "github.com/libp2p/go-libp2p/core/peer" 7 | ) 8 | 9 | type bootnodesWrapper struct { 10 | // bootnodeArr is the array that contains all the bootnode addresses 11 | bootnodeArr []*peer.AddrInfo 12 | 13 | // bootnodesMap is a map used for quick bootnode lookup 14 | bootnodesMap map[peer.ID]*peer.AddrInfo 15 | 16 | // bootnodeConnCount is an atomic value that keeps track 17 | // of the number of bootnode connections 18 | bootnodeConnCount int64 19 | } 20 | 21 | // isBootnode checks if the node ID belongs to a set bootnode 22 | func (bw *bootnodesWrapper) isBootnode(nodeID peer.ID) bool { 23 | _, ok := bw.bootnodesMap[nodeID] 24 | 25 | return ok 26 | } 27 | 28 | // getBootnodeConnCount loads the bootnode connection count [Thread safe] 29 | func (bw *bootnodesWrapper) getBootnodeConnCount() int64 { 30 | return atomic.LoadInt64(&bw.bootnodeConnCount) 31 | } 32 | 33 | // increaseBootnodeConnCount increases the bootnode connection count by delta [Thread safe] 34 | func (bw *bootnodesWrapper) increaseBootnodeConnCount(delta int64) { 35 | atomic.AddInt64(&bw.bootnodeConnCount, delta) 36 | } 37 | 38 | // getBootnodes gets all the bootnodes 39 | func (bw *bootnodesWrapper) getBootnodes() []*peer.AddrInfo { 40 | return bw.bootnodeArr 41 | } 42 | 43 | // getBootnodeCount returns the number of set bootnodes 44 | func (bw *bootnodesWrapper) getBootnodeCount() int { 45 | return len(bw.bootnodeArr) 46 | } 47 | 48 | // hasBootnodes checks if any bootnodes are set [Thread safe] 49 | func (bw *bootnodesWrapper) hasBootnodes() bool { 50 | return bw.getBootnodeCount() > 0 51 | } 52 | -------------------------------------------------------------------------------- /network/config.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/chain" 7 | "github.com/ExzoNetwork/ExzoCoin/secrets" 8 | "github.com/multiformats/go-multiaddr" 9 | ) 10 | 11 | // Config details the params for the base networking server 12 | type Config struct { 13 | NoDiscover bool // flag indicating if the discovery mechanism should be turned on 14 | Addr *net.TCPAddr // the base address 15 | NatAddr net.IP // the NAT address 16 | DNS multiaddr.Multiaddr // the DNS address 17 | DataDir string // the base data directory for the client 18 | MaxPeers int64 // the maximum number of peer connections 19 | MaxInboundPeers int64 // the maximum number of inbound peer connections 20 | MaxOutboundPeers int64 // the maximum number of outbound peer connections 21 | Chain *chain.Chain // the reference to the chain configuration 22 | SecretsManager secrets.SecretsManager // the secrets manager used for key storage 23 | } 24 | 25 | func DefaultConfig() *Config { 26 | return &Config{ 27 | // The discovery service is turned on by default 28 | NoDiscover: false, 29 | // Addresses are bound to localhost by default 30 | Addr: &net.TCPAddr{ 31 | IP: net.ParseIP("127.0.0.1"), 32 | Port: DefaultLibp2pPort, 33 | }, 34 | // The default ratio for outbound / max peer connections is 0.20 35 | MaxPeers: 40, 36 | // The default ratio for outbound / inbound connections is 0.25 37 | MaxInboundPeers: 32, 38 | MaxOutboundPeers: 8, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /network/dial/dial_task.go: -------------------------------------------------------------------------------- 1 | package dial 2 | 3 | import "github.com/libp2p/go-libp2p/core/peer" 4 | 5 | type DialTask struct { 6 | index int 7 | 8 | // info of the task 9 | addrInfo *peer.AddrInfo 10 | 11 | // priority of the task (the higher the better) 12 | priority uint64 13 | } 14 | 15 | // GetAddrInfo returns the peer information associated with the dial 16 | func (dt *DialTask) GetAddrInfo() *peer.AddrInfo { 17 | return dt.addrInfo 18 | } 19 | -------------------------------------------------------------------------------- /network/dial/heap.go: -------------------------------------------------------------------------------- 1 | package dial 2 | 3 | // The DialQueue is implemented as priority queue which utilizes a heap (standard Go implementation) 4 | // https://golang.org/src/container/heap/heap.go 5 | type dialQueueImpl []*DialTask 6 | 7 | // Len returns the length of the queue 8 | func (t dialQueueImpl) Len() int { return len(t) } 9 | 10 | // Less compares the priorities of two tasks at the passed in indexes (A < B) 11 | func (t dialQueueImpl) Less(i, j int) bool { 12 | return t[i].priority < t[j].priority 13 | } 14 | 15 | // Swap swaps the places of the tasks at the passed-in indexes 16 | func (t dialQueueImpl) Swap(i, j int) { 17 | t[i], t[j] = t[j], t[i] 18 | t[i].index = i 19 | t[j].index = j 20 | } 21 | 22 | // Push adds a new item to the queue 23 | func (t *dialQueueImpl) Push(x interface{}) { 24 | n := len(*t) 25 | item := x.(*DialTask) //nolint:forcetypeassert 26 | item.index = n 27 | *t = append(*t, item) 28 | } 29 | 30 | // Pop removes an item from the queue 31 | func (t *dialQueueImpl) Pop() interface{} { 32 | old := *t 33 | n := len(old) 34 | item := old[n-1] 35 | old[n-1] = nil 36 | item.index = -1 37 | *t = old[0 : n-1] 38 | 39 | return item 40 | } 41 | -------------------------------------------------------------------------------- /network/event/peer_event.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import "github.com/libp2p/go-libp2p/core/peer" 4 | 5 | type PeerEventType uint 6 | 7 | const ( 8 | PeerConnected PeerEventType = iota // Emitted when a peer connected 9 | PeerFailedToConnect // Emitted when a peer failed to connect 10 | PeerDisconnected // Emitted when a peer disconnected from node 11 | PeerDialCompleted // Emitted when a peer completed dial 12 | PeerAddedToDialQueue // Emitted when a peer is added to dial queue 13 | ) 14 | 15 | var peerEventToName = map[PeerEventType]string{ 16 | PeerConnected: "PeerConnected", 17 | PeerFailedToConnect: "PeerFailedToConnect", 18 | PeerDisconnected: "PeerDisconnected", 19 | PeerDialCompleted: "PeerDialCompleted", 20 | PeerAddedToDialQueue: "PeerAddedToDialQueue", 21 | } 22 | 23 | type PeerEvent struct { 24 | // PeerID is the id of the peer that triggered 25 | // the event 26 | PeerID peer.ID 27 | 28 | // Type is the type of the event 29 | Type PeerEventType 30 | } 31 | 32 | func (s PeerEventType) String() string { 33 | name, ok := peerEventToName[s] 34 | if !ok { 35 | return "unknown" 36 | } 37 | 38 | return name 39 | } 40 | -------------------------------------------------------------------------------- /network/keystore.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "encoding/hex" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/secrets" 7 | "github.com/libp2p/go-libp2p/core/crypto" 8 | ) 9 | 10 | // ReadLibp2pKey reads the private networking key from the secrets manager 11 | func ReadLibp2pKey(manager secrets.SecretsManager) (crypto.PrivKey, error) { 12 | libp2pKey, err := manager.GetSecret(secrets.NetworkKey) 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | return ParseLibp2pKey(libp2pKey) 18 | } 19 | 20 | // GenerateAndEncodeLibp2pKey generates a new networking private key, and encodes it into hex 21 | func GenerateAndEncodeLibp2pKey() (crypto.PrivKey, []byte, error) { 22 | priv, _, err := crypto.GenerateKeyPair(crypto.Secp256k1, 256) 23 | if err != nil { 24 | return nil, nil, err 25 | } 26 | 27 | buf, err := crypto.MarshalPrivateKey(priv) 28 | if err != nil { 29 | return nil, nil, err 30 | } 31 | 32 | return priv, []byte(hex.EncodeToString(buf)), nil 33 | } 34 | 35 | // ParseLibp2pKey converts a byte array to a private key 36 | func ParseLibp2pKey(key []byte) (crypto.PrivKey, error) { 37 | buf, err := hex.DecodeString(string(key)) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | libp2pKey, err := crypto.UnmarshalPrivateKey(buf) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | return libp2pKey, nil 48 | } 49 | -------------------------------------------------------------------------------- /network/proto/discovery.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package v1; 4 | 5 | option go_package = "/network/proto"; 6 | 7 | service Discovery { 8 | rpc FindPeers(FindPeersReq) returns (FindPeersResp); 9 | } 10 | 11 | message FindPeersReq { 12 | string key = 1; 13 | int64 count = 2; 14 | } 15 | 16 | message FindPeersResp { 17 | repeated string nodes = 1; 18 | } 19 | -------------------------------------------------------------------------------- /network/proto/identity.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package v1; 4 | 5 | option go_package = "/network/proto"; 6 | 7 | service Identity { 8 | rpc Hello(Status) returns (Status); 9 | } 10 | 11 | message Status { 12 | map metadata = 1; 13 | 14 | repeated Key keys = 2; 15 | 16 | int64 chain = 3; 17 | 18 | string genesis = 4; 19 | 20 | bool temporaryDial = 5; 21 | 22 | message Key { 23 | string signature = 1; 24 | string message = 2; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /network/proto/testing.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package v1; 4 | 5 | option go_package = "/network/proto"; 6 | 7 | service TestService { 8 | rpc SayHello(GenericMessage) returns (GenericMessage); 9 | rpc GetChattyServer(ChattyRequest) returns (stream GenericMessage); 10 | rpc GetChattyClient(stream GenericMessage) returns (GenericMessage); 11 | rpc GetChattyBidi(stream GenericMessage) returns (stream GenericMessage); 12 | } 13 | 14 | message GenericMessage { 15 | string message = 1; 16 | } 17 | 18 | message ChattyRequest { 19 | string message = 1; 20 | int32 count = 2; 21 | } -------------------------------------------------------------------------------- /secrets/config.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | ) 7 | 8 | // SecretsManagerConfig is the configuration that gets 9 | // written to a single configuration file 10 | type SecretsManagerConfig struct { 11 | Token string `json:"token"` // Access token to the instance 12 | ServerURL string `json:"server_url"` // The URL of the running server 13 | Type SecretsManagerType `json:"type"` // The type of SecretsManager 14 | Name string `json:"name"` // The name of the current node 15 | Namespace string `json:"namespace"` // The namespace of the service 16 | Extra map[string]interface{} `json:"extra"` // Any kind of arbitrary data 17 | } 18 | 19 | // WriteConfig writes the current configuration to the specified path 20 | func (c *SecretsManagerConfig) WriteConfig(path string) error { 21 | jsonBytes, _ := json.MarshalIndent(c, "", " ") 22 | 23 | return os.WriteFile(path, jsonBytes, os.ModePerm) 24 | } 25 | 26 | // ReadConfig reads the SecretsManagerConfig from the specified path 27 | func ReadConfig(path string) (*SecretsManagerConfig, error) { 28 | configFile, readErr := os.ReadFile(path) 29 | if readErr != nil { 30 | return nil, readErr 31 | } 32 | 33 | config := &SecretsManagerConfig{} 34 | 35 | unmarshalErr := json.Unmarshal(configFile, &config) 36 | if unmarshalErr != nil { 37 | return nil, unmarshalErr 38 | } 39 | 40 | return config, nil 41 | } 42 | -------------------------------------------------------------------------------- /secrets/secrets_test.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSupportedServiceManager(t *testing.T) { 10 | testTable := []struct { 11 | name string 12 | serviceName SecretsManagerType 13 | supported bool 14 | }{ 15 | { 16 | "Valid local secrets manager", 17 | Local, 18 | true, 19 | }, 20 | { 21 | "Valid Hashicorp Vault secrets manager", 22 | HashicorpVault, 23 | true, 24 | }, 25 | { 26 | "Valid AWS SSM secrets manager", 27 | AWSSSM, 28 | true, 29 | }, 30 | { 31 | "Valid GCP secrets manager", 32 | GCPSSM, 33 | true, 34 | }, 35 | { 36 | "Invalid secrets manager", 37 | "MarsSecretsManager", 38 | false, 39 | }, 40 | } 41 | 42 | for _, testCase := range testTable { 43 | t.Run(testCase.name, func(t *testing.T) { 44 | assert.Equal( 45 | t, 46 | testCase.supported, 47 | SupportedServiceManager(testCase.serviceName), 48 | ) 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /server/builtin.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/consensus" 5 | consensusDev "github.com/ExzoNetwork/ExzoCoin/consensus/dev" 6 | consensusDummy "github.com/ExzoNetwork/ExzoCoin/consensus/dummy" 7 | consensusIBFT "github.com/ExzoNetwork/ExzoCoin/consensus/ibft" 8 | "github.com/ExzoNetwork/ExzoCoin/secrets" 9 | "github.com/ExzoNetwork/ExzoCoin/secrets/awsssm" 10 | "github.com/ExzoNetwork/ExzoCoin/secrets/gcpssm" 11 | "github.com/ExzoNetwork/ExzoCoin/secrets/hashicorpvault" 12 | "github.com/ExzoNetwork/ExzoCoin/secrets/local" 13 | ) 14 | 15 | type ConsensusType string 16 | 17 | const ( 18 | DevConsensus ConsensusType = "dev" 19 | IBFTConsensus ConsensusType = "ibft" 20 | DummyConsensus ConsensusType = "dummy" 21 | ) 22 | 23 | var consensusBackends = map[ConsensusType]consensus.Factory{ 24 | DevConsensus: consensusDev.Factory, 25 | IBFTConsensus: consensusIBFT.Factory, 26 | DummyConsensus: consensusDummy.Factory, 27 | } 28 | 29 | // secretsManagerBackends defines the SecretManager factories for different 30 | // secret management solutions 31 | var secretsManagerBackends = map[secrets.SecretsManagerType]secrets.SecretsManagerFactory{ 32 | secrets.Local: local.SecretsManagerFactory, 33 | secrets.HashicorpVault: hashicorpvault.SecretsManagerFactory, 34 | secrets.AWSSSM: awsssm.SecretsManagerFactory, 35 | secrets.GCPSSM: gcpssm.SecretsManagerFactory, 36 | } 37 | 38 | func ConsensusSupported(value string) bool { 39 | _, ok := consensusBackends[ConsensusType(value)] 40 | 41 | return ok 42 | } 43 | -------------------------------------------------------------------------------- /server/config.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/hashicorp/go-hclog" 7 | 8 | "github.com/ExzoNetwork/ExzoCoin/chain" 9 | "github.com/ExzoNetwork/ExzoCoin/network" 10 | "github.com/ExzoNetwork/ExzoCoin/secrets" 11 | ) 12 | 13 | const DefaultGRPCPort int = 9632 14 | const DefaultJSONRPCPort int = 8545 15 | 16 | // Config is used to parametrize the minimal client 17 | type Config struct { 18 | Chain *chain.Chain 19 | 20 | JSONRPC *JSONRPC 21 | GRPCAddr *net.TCPAddr 22 | LibP2PAddr *net.TCPAddr 23 | 24 | PriceLimit uint64 25 | MaxAccountEnqueued uint64 26 | MaxSlots uint64 27 | BlockTime uint64 28 | 29 | Telemetry *Telemetry 30 | Network *network.Config 31 | 32 | DataDir string 33 | RestoreFile *string 34 | 35 | Seal bool 36 | 37 | SecretsManager *secrets.SecretsManagerConfig 38 | 39 | LogLevel hclog.Level 40 | 41 | JSONLogFormat bool 42 | 43 | LogFilePath string 44 | } 45 | 46 | // Telemetry holds the config details for metric services 47 | type Telemetry struct { 48 | PrometheusAddr *net.TCPAddr 49 | } 50 | 51 | // JSONRPC holds the config details for the JSON-RPC server 52 | type JSONRPC struct { 53 | JSONRPCAddr *net.TCPAddr 54 | AccessControlAllowOrigin []string 55 | BatchLengthLimit uint64 56 | BlockRangeLimit uint64 57 | } 58 | -------------------------------------------------------------------------------- /server/proto/system.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package v1; 4 | 5 | option go_package = "/server/proto"; 6 | 7 | import "google/protobuf/empty.proto"; 8 | 9 | service System { 10 | // GetInfo returns info about the client 11 | rpc GetStatus(google.protobuf.Empty) returns (ServerStatus); 12 | 13 | // PeersAdd adds a new peer 14 | rpc PeersAdd(PeersAddRequest) returns (PeersAddResponse); 15 | 16 | // PeersList returns the list of peers 17 | rpc PeersList(google.protobuf.Empty) returns (PeersListResponse); 18 | 19 | // PeersInfo returns the info of a peer 20 | rpc PeersStatus(PeersStatusRequest) returns (Peer); 21 | 22 | // Subscribe subscribes to blockchain events 23 | rpc Subscribe(google.protobuf.Empty) returns (stream BlockchainEvent); 24 | 25 | // Export returns blockchain data 26 | rpc BlockByNumber(BlockByNumberRequest) returns (BlockResponse); 27 | 28 | // Export returns blockchain data 29 | rpc Export(ExportRequest) returns (stream ExportEvent); 30 | } 31 | 32 | message BlockchainEvent { 33 | repeated Header added = 1; 34 | repeated Header removed = 2; 35 | 36 | message Header { 37 | int64 number = 1; 38 | string hash = 2; 39 | } 40 | } 41 | 42 | message ServerStatus { 43 | int64 network = 1; 44 | 45 | string genesis = 2; 46 | 47 | Block current = 3; 48 | 49 | string p2pAddr = 4; 50 | 51 | message Block { 52 | int64 number = 1; 53 | string hash = 2; 54 | } 55 | } 56 | 57 | message Peer { 58 | string id = 1; 59 | repeated string protocols = 2; 60 | repeated string addrs = 3; 61 | } 62 | 63 | message PeersAddRequest { 64 | string id = 1; 65 | } 66 | 67 | message PeersAddResponse { 68 | string message = 1; 69 | } 70 | 71 | message PeersStatusRequest { 72 | string id = 1; 73 | } 74 | 75 | message PeersListResponse { 76 | repeated Peer peers = 1; 77 | } 78 | 79 | message BlockByNumberRequest { 80 | uint64 number = 1; 81 | } 82 | 83 | message BlockResponse { 84 | bytes data = 1; 85 | } 86 | 87 | message ExportRequest { 88 | uint64 from = 1; 89 | uint64 to = 2; 90 | } 91 | 92 | message ExportEvent { 93 | uint64 from = 1; 94 | // null when zero 95 | uint64 to = 2; 96 | uint64 latest = 3; 97 | bytes data = 4; 98 | } 99 | -------------------------------------------------------------------------------- /state/immutable-trie/encoding.go: -------------------------------------------------------------------------------- 1 | package itrie 2 | 3 | // hasTerminator checks if hex is ending 4 | // with a terminator flag. 5 | func hasTerminator(hex []byte) bool { 6 | if len(hex) == 0 { 7 | return false 8 | } 9 | 10 | return hex[len(hex)-1] == 16 11 | } 12 | 13 | // encodeCompact packs a hex sequence (of nibbles) 14 | // into compact encoding. 15 | func encodeCompact(hex []byte) []byte { 16 | var terminator int 17 | 18 | if hasTerminator(hex) { 19 | // remove terminator flag 20 | hex = hex[:len(hex)-1] 21 | terminator = 1 22 | } else { 23 | terminator = 0 24 | } 25 | 26 | // determine prefix flag 27 | oddLen := len(hex) % 2 28 | flag := 2*terminator + oddLen 29 | 30 | // insert flag 31 | if oddLen == 1 { 32 | hex = append([]byte{byte(flag)}, hex...) 33 | } else { 34 | hex = append([]byte{byte(flag), byte(0)}, hex...) 35 | } 36 | 37 | // hex slice is of even length now - pack nibbles 38 | result := make([]byte, len(hex)/2) 39 | for i := 0; i < cap(result); i++ { 40 | result[i] = hex[2*i]<<4 | hex[2*i+1] 41 | } 42 | 43 | return result 44 | } 45 | 46 | // bytesToHexNibbles splits bytes into nibbles 47 | // (with terminator flag). Prefix flag is not removed. 48 | func bytesToHexNibbles(bytes []byte) []byte { 49 | nibbles := make([]byte, len(bytes)*2+1) 50 | for i, b := range bytes { 51 | nibbles[i*2] = b / 16 52 | nibbles[i*2+1] = b % 16 53 | } 54 | 55 | nibbles[len(nibbles)-1] = 16 56 | 57 | return nibbles 58 | } 59 | 60 | // decodeCompact unpacks compact encoding 61 | // into a hex sequence of nibbles. 62 | func decodeCompact(compact []byte) []byte { 63 | base := bytesToHexNibbles(compact) 64 | 65 | // remove the terminator flag 66 | if base[0] < 2 { 67 | base = base[:len(base)-1] 68 | } 69 | 70 | // remove prefix flag 71 | if base[0]&1 == 1 { 72 | // odd length 73 | base = base[1:] 74 | } else { 75 | // even length 76 | base = base[2:] 77 | } 78 | 79 | return base 80 | } 81 | -------------------------------------------------------------------------------- /state/immutable-trie/snapshot.go: -------------------------------------------------------------------------------- 1 | package itrie 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/crypto" 5 | "github.com/ExzoNetwork/ExzoCoin/state" 6 | "github.com/ExzoNetwork/ExzoCoin/types" 7 | "github.com/umbracle/fastrlp" 8 | ) 9 | 10 | type Snapshot struct { 11 | state *State 12 | trie *Trie 13 | } 14 | 15 | var emptyStateHash = types.StringToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") 16 | 17 | func (s *Snapshot) GetStorage(addr types.Address, root types.Hash, rawkey types.Hash) types.Hash { 18 | var ( 19 | err error 20 | trie *Trie 21 | ) 22 | 23 | if root == emptyStateHash { 24 | trie = s.state.newTrie() 25 | } else { 26 | trie, err = s.state.newTrieAt(root) 27 | if err != nil { 28 | return types.Hash{} 29 | } 30 | } 31 | 32 | key := crypto.Keccak256(rawkey.Bytes()) 33 | 34 | val, ok := trie.Get(key) 35 | if !ok { 36 | return types.Hash{} 37 | } 38 | 39 | p := &fastrlp.Parser{} 40 | 41 | v, err := p.Parse(val) 42 | if err != nil { 43 | return types.Hash{} 44 | } 45 | 46 | res := []byte{} 47 | if res, err = v.GetBytes(res[:0]); err != nil { 48 | return types.Hash{} 49 | } 50 | 51 | return types.BytesToHash(res) 52 | } 53 | 54 | func (s *Snapshot) GetAccount(addr types.Address) (*state.Account, error) { 55 | key := crypto.Keccak256(addr.Bytes()) 56 | 57 | data, ok := s.trie.Get(key) 58 | if !ok { 59 | return nil, nil 60 | } 61 | 62 | var account state.Account 63 | if err := account.UnmarshalRlp(data); err != nil { 64 | return nil, err 65 | } 66 | 67 | return &account, nil 68 | } 69 | 70 | func (s *Snapshot) GetCode(hash types.Hash) ([]byte, bool) { 71 | return s.state.GetCode(hash) 72 | } 73 | 74 | func (s *Snapshot) Commit(objs []*state.Object) (state.Snapshot, []byte) { 75 | trie, root := s.trie.Commit(objs) 76 | 77 | return &Snapshot{trie: trie, state: s.state}, root 78 | } 79 | -------------------------------------------------------------------------------- /state/immutable-trie/state_test.go: -------------------------------------------------------------------------------- 1 | package itrie 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/state" 7 | ) 8 | 9 | func TestState(t *testing.T) { 10 | state.TestState(t, buildPreState) 11 | } 12 | 13 | func buildPreState(pre state.PreStates) state.Snapshot { 14 | storage := NewMemoryStorage() 15 | st := NewState(storage) 16 | snap := st.NewSnapshot() 17 | 18 | return snap 19 | } 20 | -------------------------------------------------------------------------------- /state/runtime/evm/bitmap.go: -------------------------------------------------------------------------------- 1 | package evm 2 | 3 | const bitmapSize = uint(8) 4 | 5 | type bitmap struct { 6 | buf []byte 7 | } 8 | 9 | func (b *bitmap) isSet(i uint) bool { 10 | return b.buf[i/bitmapSize]&(1<<(i%bitmapSize)) != 0 11 | } 12 | 13 | func (b *bitmap) set(i uint) { 14 | b.buf[i/bitmapSize] |= 1 << (i % bitmapSize) 15 | } 16 | 17 | func (b *bitmap) reset() { 18 | for i := range b.buf { 19 | b.buf[i] = 0 20 | } 21 | 22 | b.buf = b.buf[:0] 23 | } 24 | 25 | func (b *bitmap) setCode(code []byte) { 26 | codeSize := uint(len(code)) 27 | b.buf = extendByteSlice(b.buf, int(codeSize/bitmapSize+1)) 28 | 29 | for i := uint(0); i < codeSize; { 30 | c := code[i] 31 | 32 | if isPushOp(c) { 33 | // push op 34 | i += uint(c - 0x60 + 2) 35 | } else { 36 | if c == 0x5B { 37 | // jumpdest 38 | b.set(i) 39 | } 40 | i++ 41 | } 42 | } 43 | } 44 | 45 | func isPushOp(i byte) bool { 46 | // From PUSH1 (0x60) to PUSH32(0x7F) 47 | return i>>5 == 3 48 | } 49 | -------------------------------------------------------------------------------- /state/runtime/evm/bitmap_test.go: -------------------------------------------------------------------------------- 1 | package evm 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestIsPush(t *testing.T) { 9 | num := 0 10 | 11 | for i := 0x0; i < 0xFF; i++ { 12 | if i>>5 == 3 { 13 | if !strings.HasPrefix(OpCode(i).String(), "PUSH") { 14 | t.Fatal("err") 15 | } 16 | num++ 17 | } 18 | } 19 | 20 | if num != 32 { 21 | t.Fatal("bad") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /state/runtime/evm/dispatch_table_test.go: -------------------------------------------------------------------------------- 1 | package evm 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPushOpcodes(t *testing.T) { 11 | code := make([]byte, 33) 12 | for i := 0; i < 33; i++ { 13 | code[i] = byte(i + 1) 14 | } 15 | 16 | c := 1 17 | 18 | for i := PUSH1; i <= PUSH32; i++ { 19 | s := &state{ 20 | code: code, 21 | } 22 | 23 | inst := dispatchTable[i] 24 | inst.inst(s) 25 | 26 | assert.False(t, s.stop) 27 | 28 | res := s.pop().Bytes() 29 | assert.Len(t, res, c) 30 | 31 | assert.True(t, bytes.HasPrefix(code[1:], res)) 32 | c++ 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /state/runtime/evm/evm.go: -------------------------------------------------------------------------------- 1 | package evm 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/chain" 7 | "github.com/ExzoNetwork/ExzoCoin/state/runtime" 8 | ) 9 | 10 | var _ runtime.Runtime = &EVM{} 11 | 12 | // EVM is the ethereum virtual machine 13 | type EVM struct { 14 | } 15 | 16 | // NewEVM creates a new EVM 17 | func NewEVM() *EVM { 18 | return &EVM{} 19 | } 20 | 21 | // CanRun implements the runtime interface 22 | func (e *EVM) CanRun(*runtime.Contract, runtime.Host, *chain.ForksInTime) bool { 23 | return true 24 | } 25 | 26 | // Name implements the runtime interface 27 | func (e *EVM) Name() string { 28 | return "evm" 29 | } 30 | 31 | // Run implements the runtime interface 32 | func (e *EVM) Run(c *runtime.Contract, host runtime.Host, config *chain.ForksInTime) *runtime.ExecutionResult { 33 | contract := acquireState() 34 | contract.resetReturnData() 35 | 36 | contract.msg = c 37 | contract.code = c.Code 38 | contract.evm = e 39 | contract.gas = c.Gas 40 | contract.host = host 41 | contract.config = config 42 | 43 | contract.bitmap.setCode(c.Code) 44 | 45 | ret, err := contract.Run() 46 | 47 | // We are probably doing this append magic to make sure that the slice doesn't have more capacity than it needs 48 | var returnValue []byte 49 | returnValue = append(returnValue[:0], ret...) 50 | 51 | gasLeft := contract.gas 52 | 53 | releaseState(contract) 54 | 55 | if err != nil && !errors.Is(err, errRevert) { 56 | gasLeft = 0 57 | } 58 | 59 | return &runtime.ExecutionResult{ 60 | ReturnValue: returnValue, 61 | GasLeft: gasLeft, 62 | GasUsed: c.Gas - gasLeft, 63 | Err: err, 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /state/runtime/evm/opcodes_test.go: -------------------------------------------------------------------------------- 1 | package evm 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestOpcodesString(t *testing.T) { 10 | assert := func(op OpCode, str string) { 11 | assert.Equal(t, op.String(), str) 12 | } 13 | 14 | assert(PUSH1, "PUSH1") 15 | assert(PUSH32, "PUSH32") 16 | 17 | assert(LOG0, "LOG0") 18 | assert(LOG4, "LOG4") 19 | 20 | assert(SWAP1, "SWAP1") 21 | assert(SWAP16, "SWAP16") 22 | 23 | assert(DUP1, "DUP1") 24 | assert(DUP16, "DUP16") 25 | 26 | assert(OpCode(0xA5), "") 27 | } 28 | -------------------------------------------------------------------------------- /state/runtime/precompiled/blake2f_test.go: -------------------------------------------------------------------------------- 1 | package precompiled 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestBlake2f(t *testing.T) { 9 | b := &blake2f{} 10 | 11 | ReadTestCase(t, "blake2f.json", func(t *testing.T, c *TestCase) { 12 | t.Helper() 13 | 14 | out, err := b.run(c.Input) 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | if !bytes.Equal(c.Expected, out) { 19 | t.Fatal("bad") 20 | } 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /state/runtime/precompiled/testing.go: -------------------------------------------------------------------------------- 1 | package precompiled 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "testing" 9 | 10 | "github.com/ExzoNetwork/ExzoCoin/helper/hex" 11 | ) 12 | 13 | type TestCase struct { 14 | Name string 15 | Input []byte 16 | Expected []byte 17 | Gas uint64 18 | } 19 | 20 | // decodeHex is a helper function for decoding a hex string 21 | func decodeHex(t *testing.T, input string) []byte { 22 | t.Helper() 23 | 24 | inputDecode, decodeErr := hex.DecodeHex(input) 25 | if decodeErr != nil { 26 | t.Fatalf("unable to decode hex, %v", decodeErr) 27 | } 28 | 29 | return inputDecode 30 | } 31 | 32 | func ReadTestCase(t *testing.T, path string, f func(t *testing.T, c *TestCase)) { 33 | t.Helper() 34 | t.Parallel() 35 | 36 | data, err := os.ReadFile(filepath.Join("./fixtures", path)) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | 41 | type testCase struct { 42 | Name string 43 | Input string 44 | Expected string 45 | Gas uint64 46 | } 47 | 48 | var cases []*testCase 49 | 50 | if err := json.Unmarshal(data, &cases); err != nil { 51 | t.Fatal(err) 52 | } 53 | 54 | for _, i := range cases { 55 | i := i 56 | 57 | inputDecode := decodeHex(t, fmt.Sprintf("0x%s", i.Input)) 58 | expectedDecode := decodeHex(t, fmt.Sprintf("0x%s", i.Expected)) 59 | 60 | c := &TestCase{ 61 | Name: i.Name, 62 | Gas: i.Gas, 63 | Input: inputDecode, 64 | Expected: expectedDecode, 65 | } 66 | 67 | t.Run(i.Name, func(t *testing.T) { 68 | t.Parallel() 69 | 70 | f(t, c) 71 | }) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /state/runtime/tracer/types.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/types" 7 | ) 8 | 9 | // RuntimeHost is the interface defining the methods for accessing state by tracer 10 | type RuntimeHost interface { 11 | // GetRefund returns refunded value 12 | GetRefund() uint64 13 | // GetStorage access the storage slot at the given address and slot hash 14 | GetStorage(types.Address, types.Hash) types.Hash 15 | } 16 | 17 | type VMState interface { 18 | // Halt tells VM to terminate its process 19 | Halt() 20 | } 21 | 22 | type Tracer interface { 23 | // Cancel tells termination of execution and tracing 24 | Cancel(error) 25 | // Clear clears the tracked data 26 | Clear() 27 | // GetResult returns a result based on tracked data 28 | GetResult() (interface{}, error) 29 | 30 | // Tx-level 31 | TxStart(gasLimit uint64) 32 | TxEnd(gasLeft uint64) 33 | 34 | // Call-level 35 | CallStart( 36 | depth int, // begins from 1 37 | from, to types.Address, 38 | callType int, 39 | gas uint64, 40 | value *big.Int, 41 | input []byte, 42 | ) 43 | CallEnd( 44 | depth int, // begins from 1 45 | output []byte, 46 | err error, 47 | ) 48 | 49 | // Op-level 50 | CaptureState( 51 | memory []byte, 52 | stack []*big.Int, 53 | opCode int, 54 | contractAddress types.Address, 55 | sp int, 56 | host RuntimeHost, 57 | state VMState, 58 | ) 59 | ExecuteState( 60 | contractAddress types.Address, 61 | ip uint64, 62 | opcode string, 63 | availableGas uint64, 64 | cost uint64, 65 | lastReturnData []byte, 66 | depth int, 67 | err error, 68 | host RuntimeHost, 69 | ) 70 | } 71 | -------------------------------------------------------------------------------- /state/txn_test.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "testing" 7 | 8 | "github.com/ExzoNetwork/ExzoCoin/types" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | type mockSnapshot struct { 13 | state map[types.Address]*PreState 14 | } 15 | 16 | func (m *mockSnapshot) GetStorage(addr types.Address, root types.Hash, key types.Hash) types.Hash { 17 | raw, ok := m.state[addr] 18 | if !ok { 19 | return types.Hash{} 20 | } 21 | 22 | res, ok := raw.State[key] 23 | if !ok { 24 | return types.Hash{} 25 | } 26 | 27 | return res 28 | } 29 | 30 | func (m *mockSnapshot) GetAccount(addr types.Address) (*Account, error) { 31 | raw, ok := m.state[addr] 32 | if !ok { 33 | return nil, fmt.Errorf("account not found") 34 | } 35 | 36 | acct := &Account{ 37 | Balance: new(big.Int).SetUint64(raw.Balance), 38 | Nonce: raw.Nonce, 39 | } 40 | 41 | return acct, nil 42 | } 43 | 44 | func (m *mockSnapshot) GetCode(hash types.Hash) ([]byte, bool) { 45 | return nil, false 46 | } 47 | 48 | func newStateWithPreState(preState map[types.Address]*PreState) readSnapshot { 49 | return &mockSnapshot{state: preState} 50 | } 51 | 52 | func newTestTxn(p map[types.Address]*PreState) *Txn { 53 | return newTxn(newStateWithPreState(p)) 54 | } 55 | 56 | func TestSnapshotUpdateData(t *testing.T) { 57 | txn := newTestTxn(defaultPreState) 58 | 59 | txn.SetState(addr1, hash1, hash1) 60 | assert.Equal(t, hash1, txn.GetState(addr1, hash1)) 61 | 62 | ss := txn.Snapshot() 63 | txn.SetState(addr1, hash1, hash2) 64 | assert.Equal(t, hash2, txn.GetState(addr1, hash1)) 65 | 66 | txn.RevertToSnapshot(ss) 67 | assert.Equal(t, hash1, txn.GetState(addr1, hash1)) 68 | } 69 | -------------------------------------------------------------------------------- /syncer/peers.go: -------------------------------------------------------------------------------- 1 | package syncer 2 | 3 | import ( 4 | "math/big" 5 | "sync" 6 | 7 | "github.com/libp2p/go-libp2p/core/peer" 8 | ) 9 | 10 | type NoForkPeer struct { 11 | // identifier 12 | ID peer.ID 13 | // peer's latest block number 14 | Number uint64 15 | // peer's distance 16 | Distance *big.Int 17 | } 18 | 19 | func (p *NoForkPeer) IsBetter(t *NoForkPeer) bool { 20 | if p.Number != t.Number { 21 | return p.Number > t.Number 22 | } 23 | 24 | return p.Distance.Cmp(t.Distance) < 0 25 | } 26 | 27 | type PeerMap struct { 28 | sync.Map 29 | } 30 | 31 | func NewPeerMap(peers []*NoForkPeer) *PeerMap { 32 | peerMap := new(PeerMap) 33 | 34 | peerMap.Put(peers...) 35 | 36 | return peerMap 37 | } 38 | 39 | func (m *PeerMap) Put(peers ...*NoForkPeer) { 40 | for _, peer := range peers { 41 | m.Store(peer.ID.String(), peer) 42 | } 43 | } 44 | 45 | // Remove removes a peer from heap if it exists 46 | func (m *PeerMap) Remove(peerID peer.ID) { 47 | m.Delete(peerID.String()) 48 | } 49 | 50 | // BestPeer returns the top of heap 51 | func (m *PeerMap) BestPeer(skipMap map[peer.ID]bool) *NoForkPeer { 52 | var bestPeer *NoForkPeer 53 | 54 | m.Range(func(key, value interface{}) bool { 55 | peer, _ := value.(*NoForkPeer) 56 | 57 | if skipMap != nil && skipMap[peer.ID] { 58 | return true 59 | } 60 | 61 | if bestPeer == nil || peer.IsBetter(bestPeer) { 62 | bestPeer = peer 63 | } 64 | 65 | return true 66 | }) 67 | 68 | return bestPeer 69 | } 70 | -------------------------------------------------------------------------------- /syncer/proto/syncer.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package v1; 4 | 5 | option go_package = "/syncer/proto"; 6 | 7 | import "google/protobuf/empty.proto"; 8 | 9 | service SyncPeer { 10 | // Returns stream of blocks beginning specified from 11 | rpc GetBlocks(GetBlocksRequest) returns (stream Block); 12 | // Returns server's status 13 | rpc GetStatus(google.protobuf.Empty) returns (SyncPeerStatus); 14 | } 15 | 16 | // GetBlocksRequest is a request for GetBlocks 17 | message GetBlocksRequest { 18 | // The height of beginning block to sync 19 | uint64 from = 1; 20 | } 21 | 22 | // Block contains a block data 23 | message Block { 24 | // RLP Encoded Block Data 25 | bytes block = 1; 26 | } 27 | 28 | // SyncPeerStatus contains peer status 29 | message SyncPeerStatus { 30 | // Latest block height 31 | uint64 number = 1; 32 | } 33 | -------------------------------------------------------------------------------- /txpool/event_queue.go: -------------------------------------------------------------------------------- 1 | package txpool 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/txpool/proto" 7 | ) 8 | 9 | type eventQueue struct { 10 | events []*proto.TxPoolEvent 11 | sync.Mutex 12 | } 13 | 14 | func (es *eventQueue) push(event *proto.TxPoolEvent) { 15 | es.Lock() 16 | defer es.Unlock() 17 | 18 | es.events = append(es.events, event) 19 | } 20 | 21 | func (es *eventQueue) pop() *proto.TxPoolEvent { 22 | es.Lock() 23 | defer es.Unlock() 24 | 25 | if len(es.events) == 0 { 26 | return nil 27 | } 28 | 29 | event := es.events[0] 30 | es.events = es.events[1:] 31 | 32 | return event 33 | } 34 | -------------------------------------------------------------------------------- /txpool/lookup_map.go: -------------------------------------------------------------------------------- 1 | package txpool 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/types" 7 | ) 8 | 9 | // Lookup map used to find transactions present in the pool 10 | type lookupMap struct { 11 | sync.RWMutex 12 | all map[types.Hash]*types.Transaction 13 | } 14 | 15 | // add inserts the given transaction into the map. Returns false 16 | // if it already exists. [thread-safe] 17 | func (m *lookupMap) add(tx *types.Transaction) bool { 18 | m.Lock() 19 | defer m.Unlock() 20 | 21 | if _, exists := m.all[tx.Hash]; exists { 22 | return false 23 | } 24 | 25 | m.all[tx.Hash] = tx 26 | 27 | return true 28 | } 29 | 30 | // remove removes the given transactions from the map. [thread-safe] 31 | func (m *lookupMap) remove(txs ...*types.Transaction) { 32 | m.Lock() 33 | defer m.Unlock() 34 | 35 | for _, tx := range txs { 36 | delete(m.all, tx.Hash) 37 | } 38 | } 39 | 40 | // get returns the transaction associated with the given hash. [thread-safe] 41 | func (m *lookupMap) get(hash types.Hash) (*types.Transaction, bool) { 42 | m.RLock() 43 | defer m.RUnlock() 44 | 45 | tx, ok := m.all[hash] 46 | if !ok { 47 | return nil, false 48 | } 49 | 50 | return tx, true 51 | } 52 | -------------------------------------------------------------------------------- /txpool/mock_test.go: -------------------------------------------------------------------------------- 1 | package txpool 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/types" 8 | ) 9 | 10 | var mockHeader = &types.Header{ 11 | GasLimit: 4712388, 12 | } 13 | 14 | /* MOCK */ 15 | 16 | type defaultMockStore struct { 17 | DefaultHeader *types.Header 18 | } 19 | 20 | func NewDefaultMockStore(header *types.Header) defaultMockStore { 21 | return defaultMockStore{ 22 | header, 23 | } 24 | } 25 | 26 | func (m defaultMockStore) Header() *types.Header { 27 | return m.DefaultHeader 28 | } 29 | 30 | func (m defaultMockStore) GetNonce(types.Hash, types.Address) uint64 { 31 | return 0 32 | } 33 | 34 | func (m defaultMockStore) GetBlockByHash(types.Hash, bool) (*types.Block, bool) { 35 | return nil, false 36 | } 37 | 38 | func (m defaultMockStore) GetBalance(types.Hash, types.Address) (*big.Int, error) { 39 | balance := big.NewInt(0).SetUint64(100000000000000) 40 | 41 | return balance, nil 42 | } 43 | 44 | type faultyMockStore struct { 45 | } 46 | 47 | func (fms faultyMockStore) Header() *types.Header { 48 | return &types.Header{} 49 | } 50 | 51 | func (fms faultyMockStore) GetNonce(root types.Hash, addr types.Address) uint64 { 52 | return 99999 53 | } 54 | 55 | func (fms faultyMockStore) GetBlockByHash(hash types.Hash, b bool) (*types.Block, bool) { 56 | return nil, false 57 | } 58 | 59 | func (fms faultyMockStore) GetBalance(root types.Hash, addr types.Address) (*big.Int, error) { 60 | return nil, fmt.Errorf("unable to fetch account state") 61 | } 62 | 63 | type mockSigner struct { 64 | } 65 | 66 | func (s *mockSigner) Sender(tx *types.Transaction) (types.Address, error) { 67 | return tx.From, nil 68 | } 69 | -------------------------------------------------------------------------------- /txpool/operator.go: -------------------------------------------------------------------------------- 1 | package txpool 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/ExzoNetwork/ExzoCoin/txpool/proto" 8 | "github.com/ExzoNetwork/ExzoCoin/types" 9 | empty "google.golang.org/protobuf/types/known/emptypb" 10 | ) 11 | 12 | // Status implements the GRPC status endpoint. Returns the number of transactions in the pool 13 | func (p *TxPool) Status(ctx context.Context, req *empty.Empty) (*proto.TxnPoolStatusResp, error) { 14 | resp := &proto.TxnPoolStatusResp{ 15 | Length: p.accounts.promoted(), 16 | } 17 | 18 | return resp, nil 19 | } 20 | 21 | // AddTxn adds a local transaction to the pool 22 | func (p *TxPool) AddTxn(ctx context.Context, raw *proto.AddTxnReq) (*proto.AddTxnResp, error) { 23 | if raw.Raw == nil { 24 | return nil, fmt.Errorf("transaction's field raw is empty") 25 | } 26 | 27 | txn := new(types.Transaction) 28 | if err := txn.UnmarshalRLP(raw.Raw.Value); err != nil { 29 | return nil, err 30 | } 31 | 32 | if raw.From != "" { 33 | from := types.Address{} 34 | if err := from.UnmarshalText([]byte(raw.From)); err != nil { 35 | return nil, err 36 | } 37 | 38 | txn.From = from 39 | } 40 | 41 | if err := p.AddTx(txn); err != nil { 42 | return nil, err 43 | } 44 | 45 | return &proto.AddTxnResp{ 46 | TxHash: txn.Hash.String(), 47 | }, nil 48 | } 49 | 50 | // Subscribe implements the operator endpoint. It subscribes to new events in the tx pool 51 | func (p *TxPool) Subscribe( 52 | request *proto.SubscribeRequest, 53 | stream proto.TxnPoolOperator_SubscribeServer, 54 | ) error { 55 | subscription := p.eventManager.subscribe(request.Types) 56 | 57 | cancel := func() { 58 | p.eventManager.cancelSubscription(subscription.subscriptionID) 59 | } 60 | 61 | for { 62 | select { 63 | case event, more := <-subscription.subscriptionChannel: 64 | if !more { 65 | // Subscription is closed from some other place 66 | return nil 67 | } 68 | 69 | if sendErr := stream.Send(event); sendErr != nil { 70 | cancel() 71 | 72 | return nil 73 | } 74 | case <-stream.Context().Done(): 75 | cancel() 76 | 77 | return nil 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /txpool/proto/operator.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package v1; 4 | 5 | option go_package = "/txpool/proto"; 6 | 7 | import "google/protobuf/any.proto"; 8 | import "google/protobuf/empty.proto"; 9 | 10 | service TxnPoolOperator { 11 | // Status returns the current status of the pool 12 | rpc Status(google.protobuf.Empty) returns (TxnPoolStatusResp); 13 | 14 | // AddTxn adds a local transaction to the pool 15 | rpc AddTxn(AddTxnReq) returns (AddTxnResp); 16 | 17 | // Subscribe subscribes for new events in the txpool 18 | rpc Subscribe(SubscribeRequest) returns (stream TxPoolEvent); 19 | } 20 | 21 | message AddTxnReq { 22 | google.protobuf.Any raw = 1; 23 | string from = 2; 24 | } 25 | 26 | message AddTxnResp { 27 | string txHash = 1; 28 | } 29 | 30 | message TxnPoolStatusResp { 31 | uint64 length = 1; 32 | } 33 | 34 | message SubscribeRequest { 35 | // Requested event types 36 | repeated EventType types = 1; 37 | } 38 | 39 | enum EventType { 40 | // For initially added transactions 41 | ADDED = 0; 42 | 43 | // For enqueued transactions in the account queue 44 | ENQUEUED = 1; 45 | 46 | // For promoted transactions 47 | PROMOTED = 2; 48 | 49 | // For dropped transactions 50 | DROPPED = 3; 51 | 52 | // For demoted transactions 53 | DEMOTED = 4; 54 | 55 | // For pruned promoted transactions 56 | PRUNED_PROMOTED = 5; 57 | 58 | // For pruned enqueued transactions 59 | PRUNED_ENQUEUED = 6; 60 | } 61 | 62 | message TxPoolEvent { 63 | EventType type = 1; 64 | string txHash = 2; 65 | } 66 | -------------------------------------------------------------------------------- /txpool/proto/v1.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package v1; 4 | 5 | option go_package = "/txpool/proto"; 6 | 7 | import "google/protobuf/any.proto"; 8 | 9 | message Txn { 10 | google.protobuf.Any raw = 1; 11 | } 12 | -------------------------------------------------------------------------------- /txpool/query.go: -------------------------------------------------------------------------------- 1 | package txpool 2 | 3 | import "github.com/ExzoNetwork/ExzoCoin/types" 4 | 5 | /* QUERY methods */ 6 | // Used to query the pool for specific state info. 7 | 8 | // GetNonce returns the next nonce for the account 9 | // 10 | // -> Returns the value from the TxPool if the account is initialized in-memory 11 | // 12 | // -> Returns the value from the world state otherwise 13 | func (p *TxPool) GetNonce(addr types.Address) uint64 { 14 | account := p.accounts.get(addr) 15 | if account == nil { 16 | stateRoot := p.store.Header().StateRoot 17 | stateNonce := p.store.GetNonce(stateRoot, addr) 18 | 19 | return stateNonce 20 | } 21 | 22 | return account.getNonce() 23 | } 24 | 25 | // GetCapacity returns the current number of slots 26 | // occupied in the pool as well as the max limit 27 | func (p *TxPool) GetCapacity() (uint64, uint64) { 28 | return p.gauge.read(), p.gauge.max 29 | } 30 | 31 | // GetPendingTx returns the transaction by hash in the TxPool (pending txn) [Thread-safe] 32 | func (p *TxPool) GetPendingTx(txHash types.Hash) (*types.Transaction, bool) { 33 | tx, ok := p.index.get(txHash) 34 | if !ok { 35 | return nil, false 36 | } 37 | 38 | return tx, true 39 | } 40 | 41 | // GetTxs gets pending and queued transactions 42 | func (p *TxPool) GetTxs(inclQueued bool) ( 43 | allPromoted, allEnqueued map[types.Address][]*types.Transaction, 44 | ) { 45 | allPromoted, allEnqueued = p.accounts.allTxs(inclQueued) 46 | 47 | return 48 | } 49 | -------------------------------------------------------------------------------- /txpool/slot_gauge.go: -------------------------------------------------------------------------------- 1 | package txpool 2 | 3 | import ( 4 | "sync/atomic" 5 | 6 | "github.com/ExzoNetwork/ExzoCoin/types" 7 | ) 8 | 9 | const ( 10 | highPressureMark = 80 // 80% 11 | ) 12 | 13 | // Gauge for measuring pool capacity in slots 14 | type slotGauge struct { 15 | height uint64 // amount of slots currently occupying the pool 16 | max uint64 // max limit 17 | } 18 | 19 | // read returns the current height of the gauge. 20 | func (g *slotGauge) read() uint64 { 21 | return atomic.LoadUint64(&g.height) 22 | } 23 | 24 | // increase increases the height of the gauge by the specified slots amount. 25 | func (g *slotGauge) increase(slots uint64) { 26 | atomic.AddUint64(&g.height, slots) 27 | } 28 | 29 | // decrease decreases the height of the gauge by the specified slots amount. 30 | func (g *slotGauge) decrease(slots uint64) { 31 | atomic.AddUint64(&g.height, ^(slots - 1)) 32 | } 33 | 34 | // highPressure checks if the gauge level 35 | // is higher than the 0.8*max threshold 36 | func (g *slotGauge) highPressure() bool { 37 | return g.read() > (highPressureMark*g.max)/100 38 | } 39 | 40 | // slotsRequired calculates the number of slots required for given transaction(s). 41 | func slotsRequired(txs ...*types.Transaction) uint64 { 42 | slots := uint64(0) 43 | for _, tx := range txs { 44 | slots += (tx.Size() + txSlotSize - 1) / txSlotSize 45 | } 46 | 47 | return slots 48 | } 49 | -------------------------------------------------------------------------------- /types/buildroot/buildroot_fast_test.go: -------------------------------------------------------------------------------- 1 | package buildroot 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "math/big" 7 | "testing" 8 | 9 | "github.com/ExzoNetwork/ExzoCoin/helper/keccak" 10 | ) 11 | 12 | func BenchmarkFast(b *testing.B) { 13 | f := acquireFastHasher() 14 | 15 | res := buildInput(128, 100) 16 | 17 | b.ResetTimer() 18 | b.ReportAllocs() 19 | 20 | for i := 0; i < b.N; i++ { 21 | f.Hash(128, res) 22 | f.reset() 23 | } 24 | } 25 | 26 | func BenchmarkSlow(b *testing.B) { 27 | res := buildInput(128, 100) 28 | 29 | b.ResetTimer() 30 | b.ReportAllocs() 31 | 32 | for i := 0; i < b.N; i++ { 33 | deriveSlow(128, res) 34 | } 35 | } 36 | 37 | func TestFastHasher(t *testing.T) { 38 | f := FastHasher{ 39 | k: keccak.NewKeccak256(), 40 | } 41 | 42 | for i := 0; i < 1000; i++ { 43 | num := randomInt(1, 128) 44 | res := buildRandomInput(int(num)) 45 | 46 | found, _ := f.Hash(int(num), res) 47 | realRes := deriveSlow(int(num), res) 48 | 49 | if !bytes.Equal(found, realRes) { 50 | t.Fatal("bad") 51 | } 52 | 53 | f.reset() 54 | } 55 | } 56 | 57 | func randomInt(min, max uint64) uint64 { 58 | randNum, _ := rand.Int(rand.Reader, big.NewInt(int64(max-min))) 59 | 60 | return min + randNum.Uint64() 61 | } 62 | 63 | func buildInput(n, m int) func(i int) []byte { 64 | res := [][]byte{} 65 | 66 | for i := 0; i < n; i++ { 67 | b := make([]byte, m) 68 | for indx := range b { 69 | b[indx] = byte(i) 70 | } 71 | 72 | res = append(res, b) 73 | } 74 | 75 | return func(i int) []byte { 76 | return res[i] 77 | } 78 | } 79 | 80 | func buildRandomInput(num int) func(i int) []byte { 81 | res := [][]byte{} 82 | 83 | for i := 0; i < num; i++ { 84 | b := make([]byte, randomInt(33, 200)) 85 | _, _ = rand.Read(b) 86 | res = append(res, b) 87 | } 88 | 89 | return func(i int) []byte { 90 | return res[i] 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /types/encoding.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/ExzoNetwork/ExzoCoin/helper/hex" 10 | ) 11 | 12 | func ParseUint64orHex(val *string) (uint64, error) { 13 | if val == nil { 14 | return 0, nil 15 | } 16 | 17 | str := *val 18 | base := 10 19 | 20 | if strings.HasPrefix(str, "0x") { 21 | str = str[2:] 22 | base = 16 23 | } 24 | 25 | return strconv.ParseUint(str, base, 64) 26 | } 27 | 28 | func ParseUint256orHex(val *string) (*big.Int, error) { 29 | if val == nil { 30 | return nil, nil 31 | } 32 | 33 | str := *val 34 | base := 10 35 | 36 | if strings.HasPrefix(str, "0x") { 37 | str = str[2:] 38 | base = 16 39 | } 40 | 41 | b, ok := new(big.Int).SetString(str, base) 42 | 43 | if !ok { 44 | return nil, fmt.Errorf("could not parse") 45 | } 46 | 47 | return b, nil 48 | } 49 | 50 | func ParseInt64orHex(val *string) (int64, error) { 51 | i, err := ParseUint64orHex(val) 52 | 53 | return int64(i), err 54 | } 55 | 56 | func ParseBytes(val *string) ([]byte, error) { 57 | if val == nil { 58 | return []byte{}, nil 59 | } 60 | 61 | str := strings.TrimPrefix(*val, "0x") 62 | 63 | return hex.DecodeString(str) 64 | } 65 | 66 | func EncodeUint64(b uint64) *string { 67 | res := fmt.Sprintf("0x%x", b) 68 | 69 | return &res 70 | } 71 | 72 | func EncodeBytes(b []byte) *string { 73 | res := "0x" + hex.EncodeToString(b) 74 | 75 | return &res 76 | } 77 | 78 | func EncodeBigInt(b *big.Int) *string { 79 | res := "0x" + b.Text(16) 80 | 81 | return &res 82 | } 83 | -------------------------------------------------------------------------------- /types/header_hash.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/helper/keccak" 5 | "github.com/umbracle/fastrlp" 6 | ) 7 | 8 | var HeaderHash func(h *Header) Hash 9 | 10 | // This is the default header hash for the block. 11 | // In IBFT, this header hash method is substituted 12 | // for Istanbul Header Hash calculation 13 | func init() { 14 | HeaderHash = defHeaderHash 15 | } 16 | 17 | var marshalArenaPool fastrlp.ArenaPool 18 | 19 | func defHeaderHash(h *Header) (hash Hash) { 20 | // default header hashing 21 | ar := marshalArenaPool.Get() 22 | hasher := keccak.DefaultKeccakPool.Get() 23 | 24 | v := h.MarshalRLPWith(ar) 25 | hasher.WriteRlp(hash[:0], v) 26 | 27 | marshalArenaPool.Put(ar) 28 | keccak.DefaultKeccakPool.Put(hasher) 29 | 30 | return 31 | } 32 | 33 | // ComputeHash computes the hash of the header 34 | func (h *Header) ComputeHash() *Header { 35 | h.Hash = HeaderHash(h) 36 | 37 | return h 38 | } 39 | -------------------------------------------------------------------------------- /types/rlp_marshal_storage.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/umbracle/fastrlp" 5 | ) 6 | 7 | type RLPStoreMarshaler interface { 8 | MarshalStoreRLPTo(dst []byte) []byte 9 | } 10 | 11 | func (b *Body) MarshalRLPTo(dst []byte) []byte { 12 | return MarshalRLPTo(b.MarshalRLPWith, dst) 13 | } 14 | 15 | func (b *Body) MarshalRLPWith(ar *fastrlp.Arena) *fastrlp.Value { 16 | vv := ar.NewArray() 17 | if len(b.Transactions) == 0 { 18 | vv.Set(ar.NewNullArray()) 19 | } else { 20 | v0 := ar.NewArray() 21 | for _, tx := range b.Transactions { 22 | v0.Set(tx.MarshalStoreRLPWith(ar)) 23 | } 24 | vv.Set(v0) 25 | } 26 | 27 | if len(b.Uncles) == 0 { 28 | vv.Set(ar.NewNullArray()) 29 | } else { 30 | v1 := ar.NewArray() 31 | for _, uncle := range b.Uncles { 32 | v1.Set(uncle.MarshalRLPWith(ar)) 33 | } 34 | vv.Set(v1) 35 | } 36 | 37 | return vv 38 | } 39 | 40 | func (t *Transaction) MarshalStoreRLPTo(dst []byte) []byte { 41 | return MarshalRLPTo(t.MarshalStoreRLPWith, dst) 42 | } 43 | 44 | func (t *Transaction) MarshalStoreRLPWith(a *fastrlp.Arena) *fastrlp.Value { 45 | vv := a.NewArray() 46 | // consensus part 47 | vv.Set(t.MarshalRLPWith(a)) 48 | // context part 49 | vv.Set(a.NewBytes(t.From.Bytes())) 50 | 51 | return vv 52 | } 53 | 54 | func (r Receipts) MarshalStoreRLPTo(dst []byte) []byte { 55 | return MarshalRLPTo(r.MarshalStoreRLPWith, dst) 56 | } 57 | 58 | func (r *Receipts) MarshalStoreRLPWith(a *fastrlp.Arena) *fastrlp.Value { 59 | vv := a.NewArray() 60 | for _, rr := range *r { 61 | vv.Set(rr.MarshalStoreRLPWith(a)) 62 | } 63 | 64 | return vv 65 | } 66 | 67 | func (r *Receipt) MarshalStoreRLPTo(dst []byte) []byte { 68 | return MarshalRLPTo(r.MarshalStoreRLPWith, dst) 69 | } 70 | 71 | func (r *Receipt) MarshalStoreRLPWith(a *fastrlp.Arena) *fastrlp.Value { 72 | // use the hash part 73 | vv := a.NewArray() 74 | vv.Set(r.MarshalRLPWith(a)) 75 | 76 | if r.ContractAddress == nil { 77 | vv.Set(a.NewNull()) 78 | } else { 79 | vv.Set(a.NewBytes(r.ContractAddress.Bytes())) 80 | } 81 | 82 | // gas used 83 | vv.Set(a.NewUint(r.GasUsed)) 84 | 85 | // TxHash 86 | vv.Set(a.NewBytes(r.TxHash.Bytes())) 87 | 88 | return vv 89 | } 90 | -------------------------------------------------------------------------------- /types/types_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "math/big" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestEIP55(t *testing.T) { 12 | t.Parallel() 13 | 14 | cases := []struct { 15 | address string 16 | expected string 17 | }{ 18 | { 19 | "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", 20 | "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", 21 | }, 22 | { 23 | "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359", 24 | "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", 25 | }, 26 | { 27 | "0xdbf03b407c01e7cd3cbea99509d93f8dddc8c6fb", 28 | "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", 29 | }, 30 | { 31 | "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb", 32 | "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", 33 | }, 34 | { 35 | "0xde64a66c41599905950ca513fa432187a8c65679", 36 | "0xde64A66C41599905950ca513Fa432187a8C65679", 37 | }, 38 | { 39 | "0xb41364957365228984ea8ee98e80dbed4b9ffcdc", 40 | "0xB41364957365228984eA8EE98e80DBED4B9fFcDC", 41 | }, 42 | { 43 | "0xb529594951753de833b00865d7fe52cc4d8b0f63", 44 | "0xB529594951753DE833b00865D7FE52cC4d8B0f63", 45 | }, 46 | { 47 | "0xb529594951753de833b00865", 48 | "0x0000000000000000B529594951753De833B00865", 49 | }, 50 | { 51 | "0xeEd210D", 52 | "0x000000000000000000000000000000000eED210d", 53 | }, 54 | } 55 | 56 | for _, c := range cases { 57 | c := c 58 | 59 | t.Run("", func(t *testing.T) { 60 | t.Parallel() 61 | 62 | addr := StringToAddress(c.address) 63 | assert.Equal(t, c.expected, addr.String()) 64 | }) 65 | } 66 | } 67 | 68 | func TestTransactionCopy(t *testing.T) { 69 | addrTo := StringToAddress("11") 70 | txn := &Transaction{ 71 | Nonce: 0, 72 | GasPrice: big.NewInt(11), 73 | Gas: 11, 74 | To: &addrTo, 75 | Value: big.NewInt(1), 76 | Input: []byte{1, 2}, 77 | V: big.NewInt(25), 78 | S: big.NewInt(26), 79 | R: big.NewInt(27), 80 | } 81 | newTxn := txn.Copy() 82 | 83 | if !reflect.DeepEqual(txn, newTxn) { 84 | t.Fatal("[ERROR] Copied transaction not equal base transaction") 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /validators/ecdsa.go: -------------------------------------------------------------------------------- 1 | package validators 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/types" 5 | "github.com/umbracle/fastrlp" 6 | ) 7 | 8 | // BLSValidator is a validator using ECDSA signing algorithm 9 | type ECDSAValidator struct { 10 | Address types.Address 11 | } 12 | 13 | // NewECDSAValidator is a constructor of ECDSAValidator 14 | func NewECDSAValidator(addr types.Address) *ECDSAValidator { 15 | return &ECDSAValidator{ 16 | Address: addr, 17 | } 18 | } 19 | 20 | // Type returns the ValidatorType of ECDSAValidator 21 | func (v *ECDSAValidator) Type() ValidatorType { 22 | return ECDSAValidatorType 23 | } 24 | 25 | // String returns string representation of ECDSAValidator 26 | func (v *ECDSAValidator) String() string { 27 | return v.Address.String() 28 | } 29 | 30 | // Addr returns the validator address 31 | func (v *ECDSAValidator) Addr() types.Address { 32 | return v.Address 33 | } 34 | 35 | // Copy returns copy of ECDSAValidator 36 | func (v *ECDSAValidator) Copy() Validator { 37 | return &ECDSAValidator{ 38 | Address: v.Address, 39 | } 40 | } 41 | 42 | // Equal checks the given validator matches with its data 43 | func (v *ECDSAValidator) Equal(vr Validator) bool { 44 | vv, ok := vr.(*ECDSAValidator) 45 | if !ok { 46 | return false 47 | } 48 | 49 | return v.Address == vv.Address 50 | } 51 | 52 | // MarshalRLPWith is a RLP Marshaller 53 | func (v *ECDSAValidator) MarshalRLPWith(arena *fastrlp.Arena) *fastrlp.Value { 54 | return arena.NewBytes(v.Address.Bytes()) 55 | } 56 | 57 | // UnmarshalRLPFrom is a RLP Unmarshaller 58 | func (v *ECDSAValidator) UnmarshalRLPFrom(p *fastrlp.Parser, val *fastrlp.Value) error { 59 | return val.GetAddr(v.Address[:]) 60 | } 61 | 62 | // Bytes returns bytes of ECDSAValidator 63 | func (v *ECDSAValidator) Bytes() []byte { 64 | return v.Address.Bytes() 65 | } 66 | 67 | // SetFromBytes parses given bytes 68 | func (v *ECDSAValidator) SetFromBytes(input []byte) error { 69 | v.Address = types.BytesToAddress(input) 70 | 71 | return nil 72 | } 73 | -------------------------------------------------------------------------------- /validators/store/snapshot/helper.go: -------------------------------------------------------------------------------- 1 | package snapshot 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/types" 5 | "github.com/ExzoNetwork/ExzoCoin/validators" 6 | ) 7 | 8 | // isAuthorize is a helper function to return the bool value from Nonce 9 | func isAuthorize( 10 | nonce types.Nonce, 11 | ) (bool, error) { 12 | switch nonce { 13 | case nonceAuthVote: 14 | return true, nil 15 | case nonceDropVote: 16 | return false, nil 17 | default: 18 | return false, ErrIncorrectNonce 19 | } 20 | } 21 | 22 | // shouldProcessVote is a helper function to return 23 | // the flag indicating whether vote should be processed or not 24 | // based on vote action and validator set 25 | func shouldProcessVote( 26 | validators validators.Validators, 27 | candidate types.Address, 28 | voteAction bool, // true => add, false => remove 29 | ) bool { 30 | // if vote action is... 31 | // true => validator set expects not to have a candidate 32 | // false => validator set expects to have a candidate 33 | return voteAction != validators.Includes(candidate) 34 | } 35 | 36 | // addsOrDelsCandidate is a helper function to add/remove candidate to/from validators 37 | func addsOrDelsCandidate( 38 | validators validators.Validators, 39 | candidate validators.Validator, 40 | updateAction bool, 41 | ) error { 42 | if updateAction { 43 | return validators.Add(candidate) 44 | } else { 45 | return validators.Del(candidate) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /validators/store/test_helper.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "github.com/ExzoNetwork/ExzoCoin/consensus/ibft/signer" 5 | "github.com/ExzoNetwork/ExzoCoin/types" 6 | ) 7 | 8 | // Utilities for test 9 | const ( 10 | TestEpochSize = 100 11 | ) 12 | 13 | func NewMockGetSigner(s signer.Signer) func(uint64) (signer.Signer, error) { 14 | return func(u uint64) (signer.Signer, error) { 15 | return s, nil 16 | } 17 | } 18 | 19 | type MockBlockchain struct { 20 | HeaderFn func() *types.Header 21 | GetHeaderByNumberFn func(uint64) (*types.Header, bool) 22 | } 23 | 24 | func (m *MockBlockchain) Header() *types.Header { 25 | return m.HeaderFn() 26 | } 27 | 28 | func (m *MockBlockchain) GetHeaderByNumber(height uint64) (*types.Header, bool) { 29 | return m.GetHeaderByNumberFn(height) 30 | } 31 | -------------------------------------------------------------------------------- /validators/types_test.go: -------------------------------------------------------------------------------- 1 | package validators 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestParseValidatorType(t *testing.T) { 10 | t.Parallel() 11 | 12 | t.Run("ECDSA", func(t *testing.T) { 13 | t.Parallel() 14 | 15 | res, err := ParseValidatorType("ecdsa") 16 | 17 | assert.Equal( 18 | t, 19 | ECDSAValidatorType, 20 | res, 21 | ) 22 | 23 | assert.NoError( 24 | t, 25 | err, 26 | ) 27 | }) 28 | 29 | t.Run("BLS", func(t *testing.T) { 30 | t.Parallel() 31 | 32 | res, err := ParseValidatorType("bls") 33 | 34 | assert.Equal( 35 | t, 36 | BLSValidatorType, 37 | res, 38 | ) 39 | 40 | assert.NoError( 41 | t, 42 | err, 43 | ) 44 | }) 45 | 46 | t.Run("other type", func(t *testing.T) { 47 | t.Parallel() 48 | 49 | _, err := ParseValidatorType("fake") 50 | 51 | assert.Equal( 52 | t, 53 | ErrInvalidValidatorType, 54 | err, 55 | ) 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /versioning/versioning.go: -------------------------------------------------------------------------------- 1 | package versioning 2 | 3 | var ( 4 | // Version is the main version at the moment. 5 | // Commit is the git commit that the binary was built on 6 | // BuildTime is the timestamp of the build 7 | // Embedded by --ldflags on build time 8 | // Versioning should follow the SemVer guidelines 9 | // https://semver.org/ 10 | Version string 11 | Branch string 12 | Commit string 13 | BuildTime string 14 | ) 15 | --------------------------------------------------------------------------------