From 94b5e7358cc0fb9242aaf480b37620d6401bb3a3 Mon Sep 17 00:00:00 2001 From: Olexander Hofman Date: Wed, 6 Mar 2024 20:05:40 +0000 Subject: [PATCH] kava v0.25.1; ethermint v0.21.0-kava-v24-0 mamoru-sniffer-go v0.12.1 --- .../workflows/cd-internal-testnet-manual.yml | 54 --- .github/workflows/cd-internal-testnet.yml | 60 ---- .github/workflows/cd-protonet.yml | 60 ---- .../workflows/cd-reset-internal-testnet.yml | 80 ----- .github/workflows/cd-seed-chain.yml | 94 ----- .github/workflows/cd-start-chain.yml | 78 ----- .github/workflows/ci-commit.yml | 7 - .github/workflows/ci-default.yml | 79 ----- .github/workflows/ci-docker.yml | 102 ------ .github/workflows/ci-lint.yml | 17 - .github/workflows/ci-master.yml | 56 --- .github/workflows/ci-pr.yml | 23 -- .github/workflows/ci-release.yml | 35 -- .github/workflows/ci-rocksdb-build.yml | 43 --- .github/workflows/mamoru-build-test.yml | 50 +++ .github/workflows/mamoru-docker.yml | 57 +++ .github/workflows/mamoru-unit-test.yml | 58 ++++ .github/workflows/metric-pipeline.yml | 45 --- .github/workflows/proto.yml | 26 -- .gitignore | 5 +- Dockerfile | 28 +- app/app.go | 8 + go.mod | 30 +- go.sum | 37 +- mamoru_cosmos_sdk/mock_streaming.go | 80 +++++ mamoru_cosmos_sdk/sniffer.go | 130 +++++++ mamoru_cosmos_sdk/sniffer_test.go | 83 +++++ mamoru_cosmos_sdk/streaming.go | 328 ++++++++++++++++++ mamoru_cosmos_sdk/streaming_test.go | 61 ++++ mamoru_cosmos_sdk/sync_state/client.go | 198 +++++++++++ mamoru_cosmos_sdk/sync_state/client_test.go | 146 ++++++++ 31 files changed, 1266 insertions(+), 892 deletions(-) delete mode 100644 .github/workflows/cd-internal-testnet-manual.yml delete mode 100644 .github/workflows/cd-internal-testnet.yml delete mode 100644 .github/workflows/cd-protonet.yml delete mode 100644 .github/workflows/cd-reset-internal-testnet.yml delete mode 100644 .github/workflows/cd-seed-chain.yml delete mode 100644 .github/workflows/cd-start-chain.yml delete mode 100644 .github/workflows/ci-commit.yml delete mode 100644 .github/workflows/ci-default.yml delete mode 100644 .github/workflows/ci-docker.yml delete mode 100644 .github/workflows/ci-lint.yml delete mode 100644 .github/workflows/ci-master.yml delete mode 100644 .github/workflows/ci-pr.yml delete mode 100644 .github/workflows/ci-release.yml delete mode 100644 .github/workflows/ci-rocksdb-build.yml create mode 100644 .github/workflows/mamoru-build-test.yml create mode 100644 .github/workflows/mamoru-docker.yml create mode 100644 .github/workflows/mamoru-unit-test.yml delete mode 100644 .github/workflows/metric-pipeline.yml delete mode 100644 .github/workflows/proto.yml create mode 100644 mamoru_cosmos_sdk/mock_streaming.go create mode 100644 mamoru_cosmos_sdk/sniffer.go create mode 100644 mamoru_cosmos_sdk/sniffer_test.go create mode 100644 mamoru_cosmos_sdk/streaming.go create mode 100644 mamoru_cosmos_sdk/streaming_test.go create mode 100644 mamoru_cosmos_sdk/sync_state/client.go create mode 100644 mamoru_cosmos_sdk/sync_state/client_test.go diff --git a/.github/workflows/cd-internal-testnet-manual.yml b/.github/workflows/cd-internal-testnet-manual.yml deleted file mode 100644 index bcb84cf95b..0000000000 --- a/.github/workflows/cd-internal-testnet-manual.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Manual Deployment (Internal Testnet) -# allow to be triggered manually -on: workflow_dispatch - -jobs: - # in order: - # enter standby (prevents autoscaling group from killing node during deploy) - # stop kava - # take ebs + zfs snapshots - # download updated binary and genesis - # reset application database state (only done on internal testnet) - reset-chain-to-zero-state: - uses: ./.github/workflows/cd-reset-internal-testnet.yml - with: - aws-region: us-east-1 - chain-id: kava_2221-17000 - ssm-document-name: kava-testnet-internal-node-update - playbook-name: reset-internal-testnet-playbook.yml - playbook-infrastructure-branch: master - secrets: inherit - - # start kava with new binary and genesis state on api, peer and seed nodes, place nodes in service once they start and are synched to live - start-chain-api: - uses: ./.github/workflows/cd-start-chain.yml - with: - aws-region: us-east-1 - chain-id: kava_2221-17000 - ssm-document-name: kava-testnet-internal-node-update - playbook-name: start-chain-api-playbook.yml - playbook-infrastructure-branch: master - secrets: inherit - needs: [reset-chain-to-zero-state] - - # setup test and development accounts and balances, deploy contracts by calling the chain's api - seed-chain-state: - uses: ./.github/workflows/cd-seed-chain.yml - with: - chain-api-url: https://rpc.app.internal.testnet.us-east.production.kava.io:443 - chain-id: kava_2221-17000 - seed-script-filename: seed-internal-testnet.sh - erc20-deployer-network-name: internal_testnet - genesis_validator_addresses: "kavavaloper1xcgtffvv2yeqmgs3yz4gv29kgjrj8usxrnrlwp kavavaloper1w66m9hdzwgd6uc8g93zqkcumgwzrpcw958sh3s" - kava_version_filepath: ./ci/env/kava-internal-testnet/KAVA.VERSION - secrets: inherit - needs: [start-chain-api] - post-pipeline-metrics: - uses: ./.github/workflows/metric-pipeline.yml - if: always() # always run so we metric failures and successes - with: - aws-region: us-east-1 - metric-name: kava.deploys.testnet.internal - namespace: Kava/ContinuousDeployment - secrets: inherit - needs: [seed-chain-state] diff --git a/.github/workflows/cd-internal-testnet.yml b/.github/workflows/cd-internal-testnet.yml deleted file mode 100644 index 5cf04a238d..0000000000 --- a/.github/workflows/cd-internal-testnet.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Continuous Deployment (Internal Testnet) -# run after every successful CI job of new commits to the master branch -on: - workflow_run: - workflows: [Continuous Integration (Kava Master)] - types: - - completed - -jobs: - # in order: - # enter standby (prevents autoscaling group from killing node during deploy) - # stop kava - # take ebs + zfs snapshots - # download updated binary and genesis - # reset application database state (only done on internal testnet) - reset-chain-to-zero-state: - # only start cd pipeline if last ci run was successful - if: ${{ github.event.workflow_run.conclusion == 'success' }} - uses: ./.github/workflows/cd-reset-internal-testnet.yml - with: - aws-region: us-east-1 - chain-id: kava_2221-17000 - ssm-document-name: kava-testnet-internal-node-update - playbook-name: reset-internal-testnet-playbook.yml - playbook-infrastructure-branch: master - secrets: inherit - - # start kava with new binary and genesis state on api, peer and seed nodes, place nodes in service once they start and are synched to live - start-chain-api: - uses: ./.github/workflows/cd-start-chain.yml - with: - aws-region: us-east-1 - chain-id: kava_2221-17000 - ssm-document-name: kava-testnet-internal-node-update - playbook-name: start-chain-api-playbook.yml - playbook-infrastructure-branch: master - secrets: inherit - needs: [reset-chain-to-zero-state] - - # setup test and development accounts and balances, deploy contracts by calling the chain's api - seed-chain-state: - uses: ./.github/workflows/cd-seed-chain.yml - with: - chain-api-url: https://rpc.app.internal.testnet.us-east.production.kava.io:443 - chain-id: kava_2221-17000 - seed-script-filename: seed-internal-testnet.sh - erc20-deployer-network-name: internal_testnet - genesis_validator_addresses: "kavavaloper1xcgtffvv2yeqmgs3yz4gv29kgjrj8usxrnrlwp kavavaloper1w66m9hdzwgd6uc8g93zqkcumgwzrpcw958sh3s" - kava_version_filepath: ./ci/env/kava-internal-testnet/KAVA.VERSION - secrets: inherit - needs: [start-chain-api] - post-pipeline-metrics: - uses: ./.github/workflows/metric-pipeline.yml - if: always() # always run so we metric failures and successes - with: - aws-region: us-east-1 - metric-name: kava.deploys.testnet.internal - namespace: Kava/ContinuousDeployment - secrets: inherit - needs: [seed-chain-state] diff --git a/.github/workflows/cd-protonet.yml b/.github/workflows/cd-protonet.yml deleted file mode 100644 index 5da8d9a2e4..0000000000 --- a/.github/workflows/cd-protonet.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Continuous Deployment (Protonet) -# run after every successful CI job of new commits to the master branch -on: - workflow_run: - workflows: [Continuous Integration (Kava Master)] - types: - - completed - -jobs: - # in order: - # enter standby (prevents autoscaling group from killing node during deploy) - # stop kava - # take ebs + zfs snapshots - # download updated binary and genesis - # reset application database state (only done on internal testnet) - reset-chain-to-zero-state: - # only start cd pipeline if last ci run was successful - if: ${{ github.event.workflow_run.conclusion == 'success' }} - uses: ./.github/workflows/cd-reset-internal-testnet.yml - with: - aws-region: us-east-1 - chain-id: proto_2221-17000 - ssm-document-name: kava-testnet-internal-node-update - playbook-name: reset-protonet-playbook.yml - playbook-infrastructure-branch: master - secrets: inherit - - # start kava with new binary and genesis state on api, peer and seed nodes, place nodes in service once they start and are synched to live - start-chain-api: - uses: ./.github/workflows/cd-start-chain.yml - with: - aws-region: us-east-1 - chain-id: proto_2221-17000 - ssm-document-name: kava-testnet-internal-node-update - playbook-name: start-chain-api-playbook.yml - playbook-infrastructure-branch: master - secrets: inherit - needs: [reset-chain-to-zero-state] - - # setup test and development accounts and balances, deploy contracts by calling the chain's api - seed-chain-state: - uses: ./.github/workflows/cd-seed-chain.yml - with: - chain-api-url: https://rpc.app.protonet.us-east.production.kava.io:443 - chain-id: proto_2221-17000 - seed-script-filename: seed-protonet.sh - erc20-deployer-network-name: protonet - genesis_validator_addresses: "kavavaloper14w4avgdvqrlpww6l5dhgj4egfn6ln7gmtp7r2m" - kava_version_filepath: ./ci/env/kava-protonet/KAVA.VERSION - secrets: inherit - needs: [start-chain-api] - post-pipeline-metrics: - uses: ./.github/workflows/metric-pipeline.yml - if: always() # always run so we metric failures and successes - with: - aws-region: us-east-1 - metric-name: kava.deploys.testnet.proto - namespace: Kava/ContinuousDeployment - secrets: inherit - needs: [seed-chain-state] diff --git a/.github/workflows/cd-reset-internal-testnet.yml b/.github/workflows/cd-reset-internal-testnet.yml deleted file mode 100644 index 9df50d7a3f..0000000000 --- a/.github/workflows/cd-reset-internal-testnet.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Reset Internal Testnet - -on: - workflow_call: - inputs: - chain-id: - required: true - type: string - aws-region: - required: true - type: string - ssm-document-name: - required: true - type: string - playbook-name: - required: true - type: string - playbook-infrastructure-branch: - required: true - type: string - secrets: - CI_AWS_KEY_ID: - required: true - CI_AWS_KEY_SECRET: - required: true - KAVA_PRIVATE_GITHUB_ACCESS_TOKEN: - required: true - -# in order: -# enter standby (prevents autoscaling group from killing node during deploy) -# stop kava -# download updated binary and genesis -# reset application database state (only done on internal testnet) -jobs: - place-chain-nodes-on-standby: - runs-on: ubuntu-latest - steps: - - name: checkout repo from current commit - uses: actions/checkout@v4 - - name: take the chain offline - run: bash ${GITHUB_WORKSPACE}/.github/scripts/put-all-chain-nodes-on-standby.sh - env: - CHAIN_ID: ${{ inputs.chain-id }} - AWS_REGION: ${{ inputs.aws-region }} - AWS_ACCESS_KEY_ID: ${{ secrets.CI_AWS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.CI_AWS_KEY_SECRET }} - - name: checkout infrastructure repo - uses: actions/checkout@v4 - with: - repository: Kava-Labs/infrastructure - token: ${{ secrets.KAVA_PRIVATE_GITHUB_ACCESS_TOKEN }} - path: infrastructure - ref: master - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - - name: build kava node updater - run: cd infrastructure/cli/kava-node-updater && make install && cd ../../../ - - name: run reset playbook on all chain nodes - run: | - kava-node-updater \ - --debug \ - --max-retries=2 \ - --aws-ssm-document-name=$SSM_DOCUMENT_NAME \ - --infrastructure-git-pointer=$PLAYBOOK_INFRASTRUCTURE_BRANCH \ - --update-playbook-filename=$PLAYBOOK_NAME \ - --chain-id=$CHAIN_ID \ - --max-upgrade-batch-size=0 \ - --node-states=Standby \ - --wait-for-node-sync-after-upgrade=false - env: - SSM_DOCUMENT_NAME: ${{ inputs.ssm-document-name }} - PLAYBOOK_NAME: ${{ inputs.playbook-name }} - CHAIN_ID: ${{ inputs.chain-id }} - AWS_REGION: ${{ inputs.aws-region }} - AWS_ACCESS_KEY_ID: ${{ secrets.CI_AWS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.CI_AWS_KEY_SECRET }} - AWS_SDK_LOAD_CONFIG: 1 - PLAYBOOK_INFRASTRUCTURE_BRANCH: ${{ inputs.playbook-infrastructure-branch }} diff --git a/.github/workflows/cd-seed-chain.yml b/.github/workflows/cd-seed-chain.yml deleted file mode 100644 index a3efb6bd43..0000000000 --- a/.github/workflows/cd-seed-chain.yml +++ /dev/null @@ -1,94 +0,0 @@ -name: Seed Chain - -on: - workflow_call: - inputs: - chain-api-url: - required: true - type: string - chain-id: - required: true - type: string - seed-script-filename: - required: true - type: string - erc20-deployer-network-name: - required: true - type: string - genesis_validator_addresses: - required: true - type: string - kava_version_filepath: - required: true - type: string - secrets: - DEV_WALLET_MNEMONIC: - required: true - KAVA_TESTNET_GOD_MNEMONIC: - required: true - -jobs: - seed-chain-state: - runs-on: ubuntu-latest - steps: - - name: checkout repo from master - uses: actions/checkout@v4 - with: - ref: master - - name: checkout version of kava used by network - run: | - git pull -p - git checkout $(cat ${KAVA_VERSION_FILEPATH}) - env: - KAVA_VERSION_FILEPATH: ${{ inputs.kava_version_filepath }} - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - - name: build kava binary - run: make install - - name: checkout go evm tools repo - uses: actions/checkout@v4 - with: - repository: ethereum/go-ethereum - path: go-ethereum - ref: v1.10.26 - - name: install go evm tools - run: | - make - make devtools - working-directory: go-ethereum - - name: checkout kava bridge repo for deploying evm contracts - uses: actions/checkout@v4 - with: - repository: Kava-Labs/kava-bridge - path: kava-bridge - ref: main - - name: install nodeJS - uses: actions/setup-node@v3 - with: - cache: npm - node-version: 18 - cache-dependency-path: kava-bridge/contract/package.json - - name: "install ERC20 contract deployment dependencies" - run: "npm install" - working-directory: kava-bridge/contract - - name: compile default erc20 contracts - run: make compile-contracts - working-directory: kava-bridge - - name: download seed script from master - run: wget https://raw.githubusercontent.com/Kava-Labs/kava/master/.github/scripts/${SEED_SCRIPT_FILENAME} && chmod +x ${SEED_SCRIPT_FILENAME} - working-directory: kava-bridge/contract - env: - SEED_SCRIPT_FILENAME: ${{ inputs.seed-script-filename }} - - name: run seed scripts - run: bash ./${SEED_SCRIPT_FILENAME} - working-directory: kava-bridge/contract - env: - CHAIN_API_URL: ${{ inputs.chain-api-url }} - CHAIN_ID: ${{ inputs.chain-id }} - DEV_WALLET_MNEMONIC: ${{ secrets.DEV_WALLET_MNEMONIC }} - KAVA_TESTNET_GOD_MNEMONIC: ${{ secrets.KAVA_TESTNET_GOD_MNEMONIC }} - SEED_SCRIPT_FILENAME: ${{ inputs.seed-script-filename }} - ERC20_DEPLOYER_NETWORK_NAME: ${{ inputs.erc20-deployer-network-name }} - GENESIS_VALIDATOR_ADDRESSES: ${{ inputs.genesis_validator_addresses }} diff --git a/.github/workflows/cd-start-chain.yml b/.github/workflows/cd-start-chain.yml deleted file mode 100644 index 02ee306594..0000000000 --- a/.github/workflows/cd-start-chain.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Start Chain - -on: - workflow_call: - inputs: - chain-id: - required: true - type: string - aws-region: - required: true - type: string - ssm-document-name: - required: true - type: string - playbook-name: - required: true - type: string - playbook-infrastructure-branch: - required: true - type: string - secrets: - CI_AWS_KEY_ID: - required: true - CI_AWS_KEY_SECRET: - required: true - KAVA_PRIVATE_GITHUB_ACCESS_TOKEN: - required: true - -jobs: - # start kava, allow nodes to start processing requests from users once they are synced to live - serve-traffic: - runs-on: ubuntu-latest - steps: - - name: checkout repo from current commit - uses: actions/checkout@v4 - - name: take the chain offline - run: bash ${GITHUB_WORKSPACE}/.github/scripts/put-all-chain-nodes-on-standby.sh - env: - CHAIN_ID: ${{ inputs.chain-id }} - AWS_REGION: ${{ inputs.aws-region }} - AWS_ACCESS_KEY_ID: ${{ secrets.CI_AWS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.CI_AWS_KEY_SECRET }} - - name: checkout infrastructure repo - uses: actions/checkout@v4 - with: - repository: Kava-Labs/infrastructure - token: ${{ secrets.KAVA_PRIVATE_GITHUB_ACCESS_TOKEN }} - path: infrastructure - ref: master - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - - name: build kava node updater - run: cd infrastructure/cli/kava-node-updater && make install && cd ../../../ - - name: run start-chain playbook on all chain nodes - run: | - kava-node-updater \ - --debug \ - --max-retries=2 \ - --aws-ssm-document-name=$SSM_DOCUMENT_NAME \ - --infrastructure-git-pointer=$PLAYBOOK_INFRASTRUCTURE_BRANCH \ - --update-playbook-filename=$PLAYBOOK_NAME \ - --chain-id=$CHAIN_ID \ - --max-upgrade-batch-size=0 \ - --node-states=Standby \ - --wait-for-node-sync-after-upgrade=true - env: - SSM_DOCUMENT_NAME: ${{ inputs.ssm-document-name }} - PLAYBOOK_NAME: ${{ inputs.playbook-name }} - CHAIN_ID: ${{ inputs.chain-id }} - AWS_REGION: ${{ inputs.aws-region }} - AWS_ACCESS_KEY_ID: ${{ secrets.CI_AWS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.CI_AWS_KEY_SECRET }} - AWS_SDK_LOAD_CONFIG: 1 - PLAYBOOK_INFRASTRUCTURE_BRANCH: ${{ inputs.playbook-infrastructure-branch }} - - name: bring the chain online - run: bash ${GITHUB_WORKSPACE}/.github/scripts/exit-standby-all-chain-nodes.sh diff --git a/.github/workflows/ci-commit.yml b/.github/workflows/ci-commit.yml deleted file mode 100644 index 79b7f2bbff..0000000000 --- a/.github/workflows/ci-commit.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: Continuous Integration (Commit) -on: - push: -# run per commit ci checks against this commit -jobs: - lint: - uses: ./.github/workflows/ci-lint.yml diff --git a/.github/workflows/ci-default.yml b/.github/workflows/ci-default.yml deleted file mode 100644 index 84806729a7..0000000000 --- a/.github/workflows/ci-default.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Continuous Integration (Default Checks) - -on: - workflow_call: -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: checkout repo from current commit - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - cache-dependency-path: | - go.sum - tests/e2e/kvtool/go.sum - - name: build application - run: make build - test: - runs-on: ubuntu-latest - steps: - - name: checkout repo from current commit - uses: actions/checkout@v4 - with: - submodules: true - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - cache-dependency-path: | - go.sum - tests/e2e/kvtool/go.sum - - name: run unit tests - run: make test - - name: run e2e tests - run: make docker-build test-e2e - validate-internal-testnet-genesis: - runs-on: ubuntu-latest - steps: - - name: checkout repo from current commit - uses: actions/checkout@v4 - - name: save version of kava that will be deployed if this pr is merged - id: kava-version - run: | - echo "KAVA_VERSION=$(cat ./ci/env/kava-internal-testnet/KAVA.VERSION)" >> $GITHUB_OUTPUT - - name: checkout repo from master - uses: actions/checkout@v4 - with: - ref: master - - name: checkout version of kava that will be deployed if this pr is merged - run: | - git pull -p - git checkout $KAVA_VERSION - env: - KAVA_VERSION: ${{ steps.kava-version.outputs.KAVA_VERSION }} - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - - name: build kava cli - run: make install - - name: checkout repo from current commit to validate current branch's genesis - uses: actions/checkout@v4 - - name: validate testnet genesis - run: kava validate-genesis ci/env/kava-internal-testnet/genesis.json - validate-protonet-genesis: - runs-on: ubuntu-latest - steps: - - name: checkout repo from current commit - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - - name: build kava cli - run: make install - - name: validate protonet genesis - run: kava validate-genesis ci/env/kava-protonet/genesis.json diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml deleted file mode 100644 index 067f605594..0000000000 --- a/.github/workflows/ci-docker.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Build & Publish Docker Images - -on: - workflow_call: - inputs: - dockerhub-username: - required: true - type: string - # this workflow publishes a rocksdb & goleveldb docker images with these tags: - # - -goleveldb - # - -goleveldb - # - -rocksdb - # - -rocksdb - extra-image-tag: - required: true - type: string - secrets: - CI_DOCKERHUB_TOKEN: - required: true - -# runs in ci-master after successful checks -# you can use images built by this action in future jobs. -# https://docs.docker.com/build/ci/github-actions/examples/#share-built-image-between-jobs -jobs: - docker-goleveldb: - # https://github.com/marketplace/actions/build-and-push-docker-images - runs-on: ubuntu-latest - steps: - # ensure working with latest code - - name: Checkout - uses: actions/checkout@v4 - - # generate a git commit hash to be used as image tag - - name: Generate short hash - id: commit-hash - run: echo "short=$( git rev-parse --short $GITHUB_SHA )" >> $GITHUB_OUTPUT - - # qemu is used to emulate different platform architectures - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - # cross-platform build of the image - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - # authenticate for publish to docker hub - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ inputs.dockerhub-username }} - password: ${{ secrets.CI_DOCKERHUB_TOKEN }} - - # publish to docker hub, tag with short git hash - - name: Build and push (goleveldb) - uses: docker/build-push-action@v5 - with: - context: . - cache-from: type=gha - cache-to: type=gha,mode=max - platforms: linux/amd64,linux/arm64 - push: true - tags: kava/kava:${{ steps.commit-hash.outputs.short }}-goleveldb,kava/kava:${{ inputs.extra-image-tag }}-goleveldb - - docker-rocksdb: - # https://github.com/marketplace/actions/build-and-push-docker-images - runs-on: ubuntu-latest - steps: - # ensure working with latest code - - name: Checkout - uses: actions/checkout@v4 - - # generate a git commit hash to be used as image tag - - name: Generate short hash - id: commit-hash - run: echo "short=$( git rev-parse --short $GITHUB_SHA )" >> $GITHUB_OUTPUT - - # qemu is used to emulate different platform architectures - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - # cross-platform build of the image - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - # authenticate for publish to docker hub - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ inputs.dockerhub-username }} - password: ${{ secrets.CI_DOCKERHUB_TOKEN }} - - # publish to docker hub, tag with short git hash - - name: Build and push (rocksdb) - uses: docker/build-push-action@v5 - with: - context: . - file: Dockerfile-rocksdb - cache-from: type=gha - cache-to: type=gha,mode=max - platforms: linux/amd64,linux/arm64 - push: true - tags: kava/kava:${{ steps.commit-hash.outputs.short }}-rocksdb,kava/kava:${{ inputs.extra-image-tag }}-rocksdb diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml deleted file mode 100644 index 7c36c83587..0000000000 --- a/.github/workflows/ci-lint.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Lint Checks -on: - workflow_call: -# run per commit ci checks against this commit -jobs: - proto-lint: - uses: ./.github/workflows/proto.yml - golangci-lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: golangci-lint - uses: reviewdog/action-golangci-lint@v2 - with: - github_token: ${{ secrets.github_token }} - reporter: github-pr-review - golangci_lint_flags: --timeout 10m diff --git a/.github/workflows/ci-master.yml b/.github/workflows/ci-master.yml deleted file mode 100644 index 0862faaba6..0000000000 --- a/.github/workflows/ci-master.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Continuous Integration (Kava Master) -on: - push: - # run CI on any push to the master branch - branches: - - master -jobs: - # run per commit ci checks against master branch - lint-checks: - uses: ./.github/workflows/ci-lint.yml - # run default ci checks against master branch - default-checks: - uses: ./.github/workflows/ci-default.yml - # build and upload versions of kava for use on internal infrastructure - # configurations for databases, cpu architectures and operating systems - publish-internal: - # only run if all checks pass - needs: [lint-checks, default-checks] - runs-on: ubuntu-latest - steps: - - name: checkout repo from current commit - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - - name: set build tag - run: echo "BUILD_TAG=$(date +%s)-$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV - - name: build rocksdb dependency - run: bash ${GITHUB_WORKSPACE}/.github/scripts/install-rocksdb.sh - env: - ROCKSDB_VERSION: v8.1.1 - - name: Build and upload release artifacts - run: bash ${GITHUB_WORKSPACE}/.github/scripts/publish-internal-release-artifacts.sh - env: - BUILD_TAG: ${{ env.BUILD_TAG }} - AWS_REGION: us-east-1 - AWS_ACCESS_KEY_ID: ${{ secrets.CI_AWS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.CI_AWS_KEY_SECRET }} - docker: - # only run if all checks pass - needs: [lint-checks, default-checks] - uses: ./.github/workflows/ci-docker.yml - with: - dockerhub-username: kavaops - extra-image-tag: master - secrets: inherit - post-pipeline-metrics: - uses: ./.github/workflows/metric-pipeline.yml - if: always() # always run so we metric failures and successes - with: - aws-region: us-east-1 - metric-name: kava.releases.merge - namespace: Kava/ContinuousIntegration - secrets: inherit - needs: [publish-internal] diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml deleted file mode 100644 index 5222c7888a..0000000000 --- a/.github/workflows/ci-pr.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Continuous Integration (PR) -on: - pull_request: - # run CI on pull requests to master or a release branch - branches: - - master - - 'release/**' - - 'releases/**' -# run default ci checks against current PR -jobs: - default: - uses: ./.github/workflows/ci-default.yml - rocksdb: - uses: ./.github/workflows/ci-rocksdb-build.yml - post-pipeline-metrics: - uses: ./.github/workflows/metric-pipeline.yml - if: always() # always run so we metric failures and successes - with: - aws-region: us-east-1 - metric-name: kava.releases.pr - namespace: Kava/ContinuousIntegration - secrets: inherit - needs: [default] diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml deleted file mode 100644 index bc8aa95540..0000000000 --- a/.github/workflows/ci-release.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Continuous Integration (Release) -on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]+*" -jobs: - # run per commit ci checks against released version - lint-checks: - uses: ./.github/workflows/ci-lint.yml - # run default ci checks against released version - default-checks: - uses: ./.github/workflows/ci-default.yml - - # get the version tag that triggered this workflow - get-version-tag: - # prep version release only if all checks pass - needs: [lint-checks, default-checks] - runs-on: ubuntu-latest - outputs: - git-tag: ${{ steps.git-tag.outputs.tag }} - steps: - - uses: actions/checkout@v4 - - id: git-tag - run: echo "tag=$(git describe --always --tags --match='v*')" >> $GITHUB_OUTPUT - - # build and upload versions of kava for use on internal infrastructure - # configurations for databases, cpu architectures and operating systems - docker: - # only run if all checks pass - needs: get-version-tag - uses: ./.github/workflows/ci-docker.yml - with: - dockerhub-username: kavaops - extra-image-tag: ${{ needs.get-version-tag.outputs.git-tag }} - secrets: inherit diff --git a/.github/workflows/ci-rocksdb-build.yml b/.github/workflows/ci-rocksdb-build.yml deleted file mode 100644 index c7cff74c36..0000000000 --- a/.github/workflows/ci-rocksdb-build.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Continuous Integration (Rocksdb Build) - -env: - ROCKSDB_VERSION: v8.1.1 - -on: - workflow_call: -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: checkout repo from current commit - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - - name: build rocksdb dependency - run: bash ${GITHUB_WORKSPACE}/.github/scripts/install-rocksdb.sh - - name: build application - run: make build COSMOS_BUILD_OPTIONS=rocksdb - test: - runs-on: ubuntu-latest - steps: - - name: install RocksDB dependencies - run: sudo apt-get update - && sudo apt-get install -y git make gcc libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev - - name: install RocksDB as shared library - run: git clone https://github.com/facebook/rocksdb.git - && cd rocksdb - && git checkout $ROCKSDB_VERSION - && sudo make -j$(nproc) install-shared - && sudo ldconfig - - name: checkout repo from current commit - uses: actions/checkout@v4 - with: - submodules: true - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - - name: run unit tests - run: make test-rocksdb diff --git a/.github/workflows/mamoru-build-test.yml b/.github/workflows/mamoru-build-test.yml new file mode 100644 index 0000000000..3df732c24f --- /dev/null +++ b/.github/workflows/mamoru-build-test.yml @@ -0,0 +1,50 @@ +name: Build Test + +on: + push: + branches: + - master + - 'mamoru*' + - develop + + + pull_request: + branches: + - master + - 'mamoru*' + - develop + + +jobs: + build-test: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version-file: go.mod + - run: go version + + - name: Cache Go modules and build cache + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Setup SSH for Private Repository Access + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.MAMORU_ETHERMINT_SSH_PRIVATE_KEY }} + + - name: Test and Build with Makefile + run: | + export GOPRIVATE=github.com/Mamoru-Foundation/* + go mod download + make build diff --git a/.github/workflows/mamoru-docker.yml b/.github/workflows/mamoru-docker.yml new file mode 100644 index 0000000000..d503c4bf6d --- /dev/null +++ b/.github/workflows/mamoru-docker.yml @@ -0,0 +1,57 @@ +name: "Build docker image" +on: + push: + branches: + - master + - 'mamoru*' + - develop + +env: + REPOSITORY: mamorufoundation/kava-sniffer + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Get current date + id: date + run: echo "::set-output name=date::$(date -u +'%Y-%m-%d')" + + - uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.MAMORU_ETHERMINT_SSH_PRIVATE_KEY }} + + - name: Prepare git and ssh config for build context + run: | + mkdir -p root-config + cp -r ~/.gitconfig ~/.ssh root-config/ + + - name: Build and push + uses: docker/build-push-action@v3 + with: + context: . + file: ./Dockerfile + build-args: | + GIT_REVISION=${{ github.sha }} + BUILD_DATE=${{ steps.date.outputs.date }} + PROFILE=release + COMMIT=${{ github.sha }} + push: true + tags: | + ${{ env.REPOSITORY }}:latest + ${{ env.REPOSITORY }}:${{ github.sha }} + ssh: | + default=${{ env.SSH_AUTH_SOCK }} diff --git a/.github/workflows/mamoru-unit-test.yml b/.github/workflows/mamoru-unit-test.yml new file mode 100644 index 0000000000..11d2c8db7b --- /dev/null +++ b/.github/workflows/mamoru-unit-test.yml @@ -0,0 +1,58 @@ +name: Unit Test + +on: + push: + branches: + - master + - 'mamoru*' + - develop + + pull_request: + branches: + - master + - 'mamoru*' + - develop + +jobs: + unit-test: + + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version-file: go.mod + - run: go version + + - uses: actions/cache@v3 + with: + # In order: + # * Module download cache + # * Build cache (Linux) + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Setup SSH for Private Repository Access + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.MAMORU_ETHERMINT_SSH_PRIVATE_KEY }} + + - name: Unit Test + run: | + export GOPRIVATE=github.com/Mamoru-Foundation/* + go mod download + make test + + + + + + diff --git a/.github/workflows/metric-pipeline.yml b/.github/workflows/metric-pipeline.yml deleted file mode 100644 index 0cd059dc31..0000000000 --- a/.github/workflows/metric-pipeline.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Metric Pipeline - -on: - workflow_call: - inputs: - aws-region: - required: true - type: string - metric-name: - required: true - type: string - namespace: - required: true - type: string - secrets: - CI_AWS_KEY_ID: - required: true - CI_AWS_KEY_SECRET: - required: true - -jobs: - metric-pipeline-result: - runs-on: ubuntu-latest - if: always() # always run to capture workflow success or failure - steps: - # Make sure the secrets are stored in you repo settings - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.CI_AWS_KEY_ID }} - aws-secret-access-key: ${{ secrets.CI_AWS_KEY_SECRET }} - aws-region: ${{ inputs.aws-region }} - - name: Calculate Pipleline Success - # run this action to get the workflow conclusion - # You can get the conclusion via env (env.WORKFLOW_CONCLUSION) - # values: neutral, success, skipped, cancelled, timed_out, - # action_required, failure - uses: technote-space/workflow-conclusion-action@v3 - - name: Metric Pipleline Success - # replace TAG by the latest tag in the repository - uses: ros-tooling/action-cloudwatch-metrics@0.0.5 - with: - metric-value: ${{ env.WORKFLOW_CONCLUSION == 'success' }} - metric-name: ${{ inputs.metric-name }} - namespace: ${{ inputs.namespace }} diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml deleted file mode 100644 index b1dc2051ee..0000000000 --- a/.github/workflows/proto.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Protobuf Checks - -on: - workflow_call: - -jobs: - check-proto: - name: "Check Proto" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - - run: go mod download - - run: make install-build-deps - - run: make check-proto-deps - - run: make check-proto-lint - - run: make check-proto-format - - run: make check-proto-breaking-remote - - run: BUF_CHECK_BREAKING_AGAINST_REMOTE="branch=$GITHUB_BASE_REF" make check-proto-breaking-remote - if: github.event_name == 'pull_request' - - run: make check-proto-gen - - run: make check-proto-gen-doc - - run: make check-proto-gen-swagger diff --git a/.gitignore b/.gitignore index a2a2088c2e..bd69dcb71f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,11 @@ *.dll *.so *.dylib - +/tmp +/tmp_daemon_storage # Test binary, build with `go test -c` *.test - +/root-config # Output of the go coverage tool *.out cover.html diff --git a/Dockerfile b/Dockerfile index 2d58dfcfd1..600aa1090e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,29 @@ -FROM golang:1.20-alpine AS build-env +#FROM golang:1.20-alpine AS build-env +FROM golang:1.20 as build-env # Set up dependencies # bash, jq, curl for debugging # git, make for installation # libc-dev, gcc, linux-headers, eudev-dev are used for cgo and ledger installation -RUN apk add bash git make libc-dev gcc linux-headers eudev-dev jq curl - +#RUN apk add bash git make libc-dev gcc linux-headers eudev-dev jq curl +RUN apt-get update \ + && apt-get install -y gcc make git curl jq git tar bash libc6-dev # Set working directory for the build WORKDIR /root/kava # default home directory is /root +# Copy the two files in place and fix different path/locations inside the Docker image +COPY root-config /root/ +RUN sed 's|/home/runner|/root|g' -i.bak /root/.ssh/config +RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts + # Copy dependency files first to facilitate dependency caching COPY ./go.mod ./ COPY ./go.sum ./ # Download dependencies -RUN --mount=type=cache,target=/root/.cache/go-build \ +RUN --mount=type=ssh \ + --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ go version && go mod download @@ -29,9 +37,17 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ make install -FROM alpine:3.15 +#FROM alpine:3.15 +FROM debian:12.0-slim + +RUN touch /var/run/supervisor.sock + +#RUN apk add bash jq curl +RUN apt-get update \ + && apt-get install -y ca-certificates jq unzip bash grep curl sed htop procps cron supervisor \ + && apt-get clean + -RUN apk add bash jq curl COPY --from=build-env /go/bin/kava /bin/kava CMD ["kava"] diff --git a/app/app.go b/app/app.go index f36b426d2d..31a91d6f37 100644 --- a/app/app.go +++ b/app/app.go @@ -2,6 +2,7 @@ package app import ( "fmt" + "github.com/kava-labs/kava/mamoru_cosmos_sdk" "io" stdlog "log" "net/http" @@ -954,6 +955,13 @@ func NewApp( // It needs to be called after `app.mm` and `app.configurator` are set. app.RegisterUpgradeHandlers() + ////////////////////////// MAMORU SNIFFER ////////////////////////// + getTStoreFunc := func(ctx sdk.Context) sdk.KVStore { + return ctx.TransientStore(tkeys[evmtypes.TransientKey]) + } + bApp.SetStreamingService(mamoru_cosmos_sdk.NewStreamingService(logger, mamoru_cosmos_sdk.NewSniffer(logger), getTStoreFunc)) + ////////////////////////// MAMORU SNIFFER ////////////////////////// + // create the simulation manager and define the order of the modules for deterministic simulations // // NOTE: This is not required for apps that don't use the simulator for fuzz testing diff --git a/go.mod b/go.mod index 3417248a44..9cbf3ff2a8 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/subosito/gotenv v1.4.2 github.com/tendermint/tendermint v0.34.27 github.com/tendermint/tm-db v0.6.7 - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.21.0 google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 google.golang.org/grpc v1.58.3 google.golang.org/protobuf v1.31.0 @@ -87,7 +87,6 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/gin-gonic/gin v1.8.1 // indirect - github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/validator/v10 v10.11.1 // indirect @@ -129,13 +128,13 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.15.15 // indirect + github.com/klauspost/compress v1.17.7 // indirect github.com/lib/pq v1.10.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect @@ -181,12 +180,12 @@ require ( go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect - golang.org/x/net v0.17.0 // indirect + golang.org/x/net v0.22.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.128.0 // indirect google.golang.org/appengine v1.6.7 // indirect @@ -209,7 +208,7 @@ replace ( // See https://github.com/cosmos/cosmos-sdk/pull/13093 github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2 // Use ethermint fork that respects min-gas-price with NoBaseFee true and london enabled, and includes eip712 support - github.com/evmos/ethermint => github.com/kava-labs/ethermint v0.21.0-kava-v24-0 + //github.com/evmos/ethermint => github.com/kava-labs/ethermint v0.21.0-kava-v24-0 // See https://github.com/cosmos/cosmos-sdk/pull/10401, https://github.com/cosmos/cosmos-sdk/commit/0592ba6158cd0bf49d894be1cef4faeec59e8320 github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 // Use the cosmos modified protobufs @@ -221,3 +220,16 @@ replace ( // Indirect dependencies still use tendermint/tm-db github.com/tendermint/tm-db => github.com/kava-labs/tm-db v0.6.7-kava.4 ) + +require ( + github.com/Mamoru-Foundation/mamoru-sniffer-go v0.12.1 + github.com/go-kit/log v0.2.1 + gotest.tools/v3 v3.4.0 +) + +replace github.com/evmos/ethermint => github.com/Mamoru-Foundation/ethermint v0.21.0-kava-v24-0 + +//replace ( +// github.com/Mamoru-Foundation/mamoru-sniffer-go => ../mamoru-sniffer-go +// github.com/evmos/ethermint => ../ethermint +//) diff --git a/go.sum b/go.sum index a8526cc31f..efabfe8276 100644 --- a/go.sum +++ b/go.sum @@ -214,6 +214,10 @@ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Mamoru-Foundation/ethermint v0.21.0-kava-v24-0 h1:spRjh9r9Z4TC+JJHsDdqvQ2niF1KMK4fKAK9b+uupuU= +github.com/Mamoru-Foundation/ethermint v0.21.0-kava-v24-0/go.mod h1:rdm6AinxZ4dzPEv/cjH+/AGyTbKufJ3RE7M2MDyklH0= +github.com/Mamoru-Foundation/mamoru-sniffer-go v0.12.1 h1:IuQkAngj38miEswwB3wb+fNwynB7Rseq8anC8tvO43Q= +github.com/Mamoru-Foundation/mamoru-sniffer-go v0.12.1/go.mod h1:u2UBuNW7Wxz5sL533/hygPYIt25EDGmWzoUuZ9XqtGo= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -808,8 +812,6 @@ github.com/kava-labs/cometbft-db v0.7.0-rocksdb-v7.9.2-kava.1 h1:EZnZAkZ+dqK+1OM github.com/kava-labs/cometbft-db v0.7.0-rocksdb-v7.9.2-kava.1/go.mod h1:mI/4J4IxRzPrXvMiwefrt0fucGwaQ5Hm9IKS7HnoJeI= github.com/kava-labs/cosmos-sdk v0.46.11-kava.3 h1:TOhyyW/xHso/9uIOgYdsrOWDIhXi6foORWZxVRe/wS0= github.com/kava-labs/cosmos-sdk v0.46.11-kava.3/go.mod h1:bSUUbmVwWkv1ZNVTWrQHa/i+73xIUvYYPsCvl5doiCs= -github.com/kava-labs/ethermint v0.21.0-kava-v24-0 h1:bIEw/wkmgNx2GxaQjAa/nbIuGEwcvBBU15QvR5C3Fow= -github.com/kava-labs/ethermint v0.21.0-kava-v24-0/go.mod h1:rdm6AinxZ4dzPEv/cjH+/AGyTbKufJ3RE7M2MDyklH0= github.com/kava-labs/tm-db v0.6.7-kava.4 h1:M2RibOKmbi+k2OhAFry8z9+RJF0CYuDETB7/PrSdoro= github.com/kava-labs/tm-db v0.6.7-kava.4/go.mod h1:70tpLhNfwCP64nAlq+bU+rOiVfWr3Nnju1D1nhGDGKs= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -820,8 +822,8 @@ github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -878,8 +880,8 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -1246,8 +1248,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1358,8 +1360,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1508,14 +1510,15 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1527,8 +1530,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1891,6 +1894,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/mamoru_cosmos_sdk/mock_streaming.go b/mamoru_cosmos_sdk/mock_streaming.go new file mode 100644 index 0000000000..42be958646 --- /dev/null +++ b/mamoru_cosmos_sdk/mock_streaming.go @@ -0,0 +1,80 @@ +package mamoru_cosmos_sdk + +import ( + "context" + + tmabci "github.com/tendermint/tendermint/abci/types" + "sync" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + tmlog "github.com/tendermint/tendermint/libs/log" + //"github.com/Mamoru-Foundation/mamoru-sniffer-go/mamoru_sniffer" +) + +var _ baseapp.StreamingService = (*MockStreamingService)(nil) + +// MockStreamingService mock streaming service +type MockStreamingService struct { + logger tmlog.Logger + currentBlockNumber int64 + storeListeners []*types.MemoryListener +} + +func NewMockStreamingService(logger tmlog.Logger) *MockStreamingService { + logger.Info("Mamoru MockStreamingService start") + + //_ = mamoru_sniffer.LogLevel(1) + + storeKeys := []types.StoreKey{sdk.NewKVStoreKey("mamoru")} + listeners := make([]*types.MemoryListener, len(storeKeys)) + for i, key := range storeKeys { + listeners[i] = types.NewMemoryListener(key) + } + return &MockStreamingService{ + logger: logger, + storeListeners: listeners, + } +} + +func (ss *MockStreamingService) ListenBeginBlock(ctx context.Context, req tmabci.RequestBeginBlock, res tmabci.ResponseBeginBlock) error { + ss.currentBlockNumber = req.Header.Height + ss.logger.Info("Mamoru Mock ListenBeginBlock", "height", ss.currentBlockNumber) + + return nil +} + +func (ss *MockStreamingService) ListenDeliverTx(ctx context.Context, req tmabci.RequestDeliverTx, res tmabci.ResponseDeliverTx) error { + ss.logger.Info("Mamoru Mock ListenDeliverTx", "height", ss.currentBlockNumber) + + return nil +} + +func (ss *MockStreamingService) ListenEndBlock(ctx context.Context, req tmabci.RequestEndBlock, res tmabci.ResponseEndBlock) error { + ss.logger.Info("Mamoru Mock ListenEndBlock", "height", ss.currentBlockNumber) + + return nil +} + +func (ss *MockStreamingService) ListenCommit(ctx context.Context, res tmabci.ResponseCommit) error { + ss.logger.Info("Mamoru Mock ListenCommit", "height", ss.currentBlockNumber) + + return nil +} + +func (ss *MockStreamingService) Stream(wg *sync.WaitGroup) error { + return nil +} + +func (ss *MockStreamingService) Listeners() map[types.StoreKey][]types.WriteListener { + listeners := make(map[types.StoreKey][]types.WriteListener, len(ss.storeListeners)) + for _, listener := range ss.storeListeners { + listeners[listener.StoreKey()] = []types.WriteListener{listener} + } + return listeners +} + +func (ss MockStreamingService) Close() error { + return nil +} diff --git a/mamoru_cosmos_sdk/sniffer.go b/mamoru_cosmos_sdk/sniffer.go new file mode 100644 index 0000000000..bf4e128e34 --- /dev/null +++ b/mamoru_cosmos_sdk/sniffer.go @@ -0,0 +1,130 @@ +package mamoru_cosmos_sdk + +import ( + "fmt" + "os" + "strconv" + "sync" + + "github.com/go-kit/log/level" + "github.com/go-kit/log/term" + "github.com/tendermint/tendermint/libs/log" + + "github.com/Mamoru-Foundation/mamoru-sniffer-go/mamoru_sniffer" + "github.com/Mamoru-Foundation/mamoru-sniffer-go/mamoru_sniffer/cosmos" + "github.com/kava-labs/kava/mamoru_cosmos_sdk/sync_state" +) + +const ( + PolishTimeSec = 10 + DefaultTNApiUrl = "http://localhost:26657/status" +) + +var snifferConnectFunc = cosmos.CosmosConnect + +func InitConnectFunc(f func() (*cosmos.SnifferCosmos, error)) { + snifferConnectFunc = f +} + +func init() { + mamoru_sniffer.InitLogger(func(entry mamoru_sniffer.LogEntry) { + kvs := mapToInterfaceSlice(entry.Ctx) + msg := "Mamoru core: " + entry.Message + var tmLogger = log.NewTMLoggerWithColorFn(os.Stdout, func(keyvals ...interface{}) term.FgBgColor { + if keyvals[0] != level.Key() { + panic(fmt.Sprintf("expected level key to be first, got %v", keyvals[0])) + } + switch keyvals[1].(level.Value).String() { + case "debug": + return term.FgBgColor{Fg: term.Green} + case "error": + return term.FgBgColor{Fg: term.DarkRed} + default: + return term.FgBgColor{} + } + }) + + switch entry.Level { + case mamoru_sniffer.LogLevelDebug: + tmLogger.Debug(msg, kvs...) + case mamoru_sniffer.LogLevelInfo: + tmLogger.Info(msg, kvs...) + case mamoru_sniffer.LogLevelWarning: + tmLogger.With("Warn").Error(msg, kvs...) + case mamoru_sniffer.LogLevelError: + tmLogger.Error(msg, kvs...) + } + }) +} + +func mapToInterfaceSlice(m map[string]string) []interface{} { + var result []interface{} + for key, value := range m { + result = append(result, key, value) + } + + return result +} + +type Sniffer struct { + mu sync.Mutex + logger log.Logger + client *cosmos.SnifferCosmos + sync *sync_state.Client +} + +func NewSniffer(logger log.Logger) *Sniffer { + tmApiUrl := getEnv("MAMORU_TM_API_URL", DefaultTNApiUrl) + httpClient := sync_state.NewHTTPRequest(logger, tmApiUrl, PolishTimeSec, isSnifferEnabled()) + + return &Sniffer{ + logger: logger, + sync: httpClient, + } +} + +// IsSynced returns true if the sniffer is synced with the chain +func (s *Sniffer) IsSynced() bool { + s.logger.Info("Mamoru Sniffer sync", "sync", s.sync.GetSyncData().IsSync(), + "block", s.sync.GetSyncData().GetCurrentBlockNumber()) + + return s.sync.GetSyncData().IsSync() +} + +func (s *Sniffer) CheckRequirements() bool { + return isSnifferEnabled() && s.IsSynced() && s.connect() +} + +func (s *Sniffer) Client() *cosmos.SnifferCosmos { + return s.client +} + +func (s *Sniffer) connect() bool { + if s.client != nil { + return true + } + + s.mu.Lock() + defer s.mu.Unlock() + + var err error + s.client, err = snifferConnectFunc() + if err != nil { + s.logger.Error("Mamoru Sniffer connect", "err", err) + return false + } + + return true +} + +func isSnifferEnabled() bool { + val, _ := strconv.ParseBool(getEnv("MAMORU_SNIFFER_ENABLE", "false")) + return val +} + +func getEnv(key, fallback string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return fallback +} diff --git a/mamoru_cosmos_sdk/sniffer_test.go b/mamoru_cosmos_sdk/sniffer_test.go new file mode 100644 index 0000000000..7d123bc3a5 --- /dev/null +++ b/mamoru_cosmos_sdk/sniffer_test.go @@ -0,0 +1,83 @@ +package mamoru_cosmos_sdk + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + tmprototypes "github.com/tendermint/tendermint/proto/tendermint/types" + "gotest.tools/v3/assert" + "os" + "testing" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" +) + +// TestNewSniffer tests the NewSniffer function +func TestNewSniffer(t *testing.T) { + snifferTest := NewSniffer(log.NewTMLogger(log.NewSyncWriter(os.Stdout))) + if snifferTest == nil { + t.Error("NewSniffer returned nil") + } +} + +// TestIsSnifferEnable tests the isSnifferEnable method +func TestIsSnifferEnable(t *testing.T) { + + // Set environment variable for testing + t.Setenv("MAMORU_SNIFFER_ENABLE", "true") + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + _ = NewSniffer(logger) + if !isSnifferEnabled() { + t.Error("Expected sniffer to be enabled") + } + + // Test with invalid value + t.Setenv("MAMORU_SNIFFER_ENABLE", "not_a_bool") + if isSnifferEnabled() { + t.Error("Expected sniffer to be disabled with invalid env value") + } +} + +// smoke test for the sniffer +func TestSnifferSmoke(t *testing.T) { + t.Skip() + t.Setenv("MAMORU_SNIFFER_ENABLE", "true") + t.Setenv("MAMORU_CHAIN_TYPE", "ETH_TESTNET") + t.Setenv("MAMORU_CHAIN_ID", "validationchain") + t.Setenv("MAMORU_STATISTICS_SEND_INTERVAL_SECS", "1") + t.Setenv("MAMORU_ENDPOINT", "http://localhost:9090") + t.Setenv("MAMORU_PRIVATE_KEY", "/6Hi8mqAFp14m3pySNYDjXhUysZok0X6jaMWvwZGdd8=") + //InitConnectFunc(func() (*cosmos.SnifferCosmos, error) { + // return nil, nil + //}) + logger := log.TestingLogger() + sniffer := NewSniffer(logger) + if sniffer == nil { + t.Error("NewSniffer returned nil") + } + header := tmprototypes.Header{} + ischeck := true + ctx := sdk.NewContext(nil, header, ischeck, logger) + sdkctx := sdk.UnwrapSDKContext(ctx) + + getTStoreFunc := func(ctx sdk.Context) sdk.KVStore { + return sdkctx.TransientStore(sdk.NewKVStoreKey("somekey")) + } + + streamingService := NewStreamingService(logger, sniffer, getTStoreFunc) + regBB := abci.RequestBeginBlock{} + resBB := abci.ResponseBeginBlock{} + err := streamingService.ListenBeginBlock(ctx, regBB, resBB) + assert.NilError(t, err) + regDT := abci.RequestDeliverTx{} + resDT := abci.ResponseDeliverTx{} + err = streamingService.ListenDeliverTx(ctx, regDT, resDT) + assert.NilError(t, err) + regEB := abci.RequestEndBlock{} + resEB := abci.ResponseEndBlock{} + err = streamingService.ListenEndBlock(ctx, regEB, resEB) + assert.NilError(t, err) + + resC := abci.ResponseCommit{} + err = streamingService.ListenCommit(ctx, resC) + assert.NilError(t, err) +} diff --git a/mamoru_cosmos_sdk/streaming.go b/mamoru_cosmos_sdk/streaming.go new file mode 100644 index 0000000000..5d1b5c047e --- /dev/null +++ b/mamoru_cosmos_sdk/streaming.go @@ -0,0 +1,328 @@ +package mamoru_cosmos_sdk + +import ( + "context" + "encoding/hex" + "strconv" + "strings" + "sync" + + "github.com/Mamoru-Foundation/mamoru-sniffer-go/mamoru_sniffer/cosmos" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/store/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/evmos/ethermint/x/evm/types/mamoru" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/libs/log" + types2 "github.com/tendermint/tendermint/types" +) + +var _ baseapp.StreamingService = (*StreamingService)(nil) + +type StreamingService struct { + logger log.Logger + + blockMetadata types.BlockMetadata + currentBlockNumber int64 + callFrame []*mamoru.CallFrame + + storeListeners []*types.MemoryListener + + sniffer *Sniffer + getTStoreFunc func(ctx sdktypes.Context) types.KVStore +} + +func NewStreamingService(logger log.Logger, sniffer *Sniffer, getTStoreFunc func(ctx sdktypes.Context) types.KVStore) *StreamingService { + logger.Info("Mamoru StreamingService start") + + return &StreamingService{ + sniffer: sniffer, + logger: logger, + getTStoreFunc: getTStoreFunc, + } +} + +func (ss *StreamingService) ListenBeginBlock(ctx context.Context, req abci.RequestBeginBlock, res abci.ResponseBeginBlock) error { + ss.blockMetadata = types.BlockMetadata{} + ss.blockMetadata.RequestBeginBlock = &req + ss.blockMetadata.ResponseBeginBlock = &res + ss.currentBlockNumber = req.Header.Height + ss.logger.Info("Mamoru ListenBeginBlock", "height", ss.currentBlockNumber) + + ss.callFrame = []*mamoru.CallFrame{} + + return nil +} + +func (ss *StreamingService) ListenDeliverTx(ctx context.Context, req abci.RequestDeliverTx, res abci.ResponseDeliverTx) error { + ss.logger.Info("Mamoru ListenDeliverTx", "height", ss.currentBlockNumber) + ss.blockMetadata.DeliverTxs = append(ss.blockMetadata.DeliverTxs, &types.BlockMetadata_DeliverTx{ + Request: &req, + Response: &res, + }) + + transientStore := ss.getTStoreFunc(sdktypes.UnwrapSDKContext(ctx)) + takeCallFrames(ss.logger, transientStore, ss.currentBlockNumber, &ss.callFrame) + + return nil +} + +func takeCallFrames(logger log.Logger, storage types.KVStore, blockHeight int64, callFrame *[]*mamoru.CallFrame) { + tracerId := storage.Get([]byte(mamoru.KeyName)) + if tracerId == nil { + return + } + + calls := storage.Get(mamoru.TraceID(blockHeight, string(tracerId))) + callFrameArr, err := mamoru.UnmarshalCallFrames(calls) + if err != nil { + logger.Error("Mamoru ListenDeliverTx", "error", err) + return + } + + if tracerId == nil { + return + } + + *callFrame = append(*callFrame, callFrameArr...) + logger.Info("Mamoru ListenDeliverTx", "height", blockHeight, "callFrame", len(*callFrame), "total.ss.callFrame", len(callFrameArr)) +} + +func (ss *StreamingService) ListenEndBlock(ctx context.Context, req abci.RequestEndBlock, res abci.ResponseEndBlock) error { + ss.blockMetadata.RequestEndBlock = &req + ss.blockMetadata.ResponseEndBlock = &res + ss.logger.Info("Mamoru ListenEndBlock", "height", ss.currentBlockNumber) + + return nil +} + +func (ss *StreamingService) ListenCommit(ctx context.Context, res abci.ResponseCommit) error { + if ss.sniffer == nil || !ss.sniffer.CheckRequirements() { + return nil + } + + ss.blockMetadata.ResponseCommit = &res + ss.logger.Info("Mamoru ListenCommit", "height", ss.currentBlockNumber) + + var eventCount uint64 = 0 + var txCount uint64 = 0 + var callTracesCount uint64 = 0 + builder := cosmos.NewCosmosCtxBuilder() + + blockHeight := uint64(ss.blockMetadata.RequestEndBlock.Height) + block := cosmos.Block{ + Seq: blockHeight, + Height: ss.blockMetadata.RequestEndBlock.Height, + Hash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Hash), + VersionBlock: ss.blockMetadata.RequestBeginBlock.Header.Version.Block, + VersionApp: ss.blockMetadata.RequestBeginBlock.Header.Version.App, + ChainId: ss.blockMetadata.RequestBeginBlock.Header.ChainID, + Time: ss.blockMetadata.RequestBeginBlock.Header.Time.Unix(), + LastBlockIdHash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.LastBlockId.Hash), + LastBlockIdPartSetHeaderTotal: ss.blockMetadata.RequestBeginBlock.Header.LastBlockId.PartSetHeader.Total, + LastBlockIdPartSetHeaderHash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.LastBlockId.PartSetHeader.Hash), + LastCommitHash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.LastCommitHash), + DataHash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.DataHash), + ValidatorsHash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.ValidatorsHash), + NextValidatorsHash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.NextValidatorsHash), + ConsensusHash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.ConsensusHash), + AppHash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.AppHash), + LastResultsHash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.LastResultsHash), + EvidenceHash: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.EvidenceHash), + ProposerAddress: hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Header.ProposerAddress), + LastCommitInfoRound: ss.blockMetadata.RequestBeginBlock.LastCommitInfo.Round, + } + + if ss.blockMetadata.ResponseEndBlock.ConsensusParamUpdates != nil { + block.ConsensusParamUpdatesBlockMaxBytes = ss.blockMetadata.ResponseEndBlock.ConsensusParamUpdates.Block.MaxBytes + block.ConsensusParamUpdatesBlockMaxGas = ss.blockMetadata.ResponseEndBlock.ConsensusParamUpdates.Block.MaxGas + block.ConsensusParamUpdatesEvidenceMaxAgeNumBlocks = ss.blockMetadata.ResponseEndBlock.ConsensusParamUpdates.Evidence.MaxAgeNumBlocks + block.ConsensusParamUpdatesEvidenceMaxAgeDuration = ss.blockMetadata.ResponseEndBlock.ConsensusParamUpdates.Evidence.MaxAgeDuration.Milliseconds() + block.ConsensusParamUpdatesEvidenceMaxBytes = ss.blockMetadata.ResponseEndBlock.ConsensusParamUpdates.Evidence.MaxBytes + block.ConsensusParamUpdatesValidatorPubKeyTypes = strings.Join(ss.blockMetadata.ResponseEndBlock.ConsensusParamUpdates.Validator.PubKeyTypes[:], ",") //todo []string to string + block.ConsensusParamUpdatesVersionApp = ss.blockMetadata.ResponseEndBlock.ConsensusParamUpdates.Version.GetAppVersion() + } + + builder.SetBlock(block) + + for _, beginBlock := range ss.blockMetadata.ResponseBeginBlock.Events { + eventCount++ + builder.AppendEvents([]cosmos.Event{ + { + Seq: blockHeight, + EventType: beginBlock.Type, + }, + }) + for _, attribute := range beginBlock.Attributes { + builder.AppendEventAttributes([]cosmos.EventAttribute{ + { + Seq: blockHeight, + EventSeq: blockHeight, + Key: string(attribute.Key), + Value: string(attribute.Value), + Index: attribute.Index, + }, + }) + } + } + + for _, validatorUpdate := range ss.blockMetadata.ResponseEndBlock.ValidatorUpdates { + builder.AppendValidatorUpdates([]cosmos.ValidatorUpdate{ + { + Seq: blockHeight, + PubKey: validatorUpdate.PubKey.GetEd25519(), + Power: validatorUpdate.Power, + }, + }) + } + + for _, voteInfo := range ss.blockMetadata.RequestBeginBlock.LastCommitInfo.Votes { + builder.AppendVoteInfos([]cosmos.VoteInfo{ + { + Seq: blockHeight, + BlockSeq: blockHeight, + ValidatorAddress: sdktypes.ValAddress(voteInfo.Validator.Address).String(), + ValidatorPower: voteInfo.Validator.Power, + SignedLastBlock: voteInfo.SignedLastBlock, + }, + }) + } + + for _, misbehavior := range ss.blockMetadata.RequestBeginBlock.ByzantineValidators { + builder.AppendMisbehaviors([]cosmos.Misbehavior{ + { + Seq: blockHeight, + BlockSeq: blockHeight, + Typ: misbehavior.Type.String(), + ValidatorPower: misbehavior.Validator.Power, + ValidatorAddress: sdktypes.ValAddress(misbehavior.Validator.Address).String(), + Height: misbehavior.Height, + Time: misbehavior.Time.Unix(), + TotalVotingPower: misbehavior.TotalVotingPower, + }, + }) + } + + for txIndex, tx := range ss.blockMetadata.DeliverTxs { + txHash := bytes.HexBytes(types2.Tx(tx.Request.Tx).Hash()).String() + builder.AppendTxs([]cosmos.Transaction{ + { + Seq: blockHeight, + Tx: tx.Request.Tx, + TxHash: txHash, + TxIndex: uint32(txIndex), + Code: tx.Response.Code, + Data: tx.Response.Data, + Log: tx.Response.Log, + Info: tx.Response.Info, + GasWanted: tx.Response.GasWanted, + GasUsed: tx.Response.GasUsed, + Codespace: tx.Response.Codespace, + }, + }) + + for _, call := range ss.callFrame { + callTracesCount++ + builder.AppendEvmCallTraces([]cosmos.EvmCallTrace{ + { + TxHash: txHash, + TxIndex: call.TxIndex, + BlockIndex: ss.currentBlockNumber, + Depth: call.Depth, + Type: call.Type, + From: call.From, + To: call.To, + Value: call.Value, + GasLimit: call.Gas, + GasUsed: call.GasUsed, + Input: call.Input, + Output: call.Output, + Error: call.Error, + RevertReason: call.RevertReason, + }, + }) + } + + for _, event := range tx.Response.Events { + eventCount++ + builder.AppendEvents([]cosmos.Event{ + { + Seq: blockHeight, + EventType: event.Type, + }, + }) + + for _, attribute := range event.Attributes { + builder.AppendEventAttributes([]cosmos.EventAttribute{ + { + Seq: blockHeight, + EventSeq: blockHeight, + Key: string(attribute.Key), + Value: string(attribute.Value), + Index: attribute.Index, + }, + }) + } + } + + txCount++ + } + + for _, event := range ss.blockMetadata.ResponseEndBlock.Events { + eventCount++ + builder.AppendEvents([]cosmos.Event{ + { + Seq: blockHeight, + EventType: event.Type, + }, + }) + for _, attribute := range event.Attributes { + builder.AppendEventAttributes([]cosmos.EventAttribute{ + { + Seq: blockHeight, + EventSeq: blockHeight, + Key: string(attribute.Key), + Value: string(attribute.Value), + Index: attribute.Index, + }, + }) + } + } + + builder.SetBlockData(strconv.FormatUint(blockHeight, 10), hex.EncodeToString(ss.blockMetadata.RequestBeginBlock.Hash)) + + statTxs := txCount + statEvn := eventCount + eventCount = 0 + txCount = 0 + + builder.SetStatistics(uint64(1), statTxs, statEvn, callTracesCount) + + cosmosCtx := builder.Finish() + + ss.logger.Info("Mamoru Send", "height", ss.currentBlockNumber, "txs", statTxs, "events", statEvn, "callTraces", callTracesCount) + + if client := ss.sniffer.Client(); client != nil { + client.ObserveCosmosData(cosmosCtx) + } + + return nil +} + +func (ss *StreamingService) Stream(wg *sync.WaitGroup) error { + return nil +} + +func (ss *StreamingService) Listeners() map[types.StoreKey][]types.WriteListener { + listeners := make(map[types.StoreKey][]types.WriteListener, len(ss.storeListeners)) + //for _, listener := range ss.storeListeners { + // listeners[listener.StoreKey()] = []types.WriteListener{listener} + //} + return listeners +} + +func (ss StreamingService) Close() error { + return nil +} diff --git a/mamoru_cosmos_sdk/streaming_test.go b/mamoru_cosmos_sdk/streaming_test.go new file mode 100644 index 0000000000..86363f9df6 --- /dev/null +++ b/mamoru_cosmos_sdk/streaming_test.go @@ -0,0 +1,61 @@ +package mamoru_cosmos_sdk + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "testing" + "time" + + tmabci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + tmprototypes "github.com/tendermint/tendermint/proto/tendermint/types" + tmversion "github.com/tendermint/tendermint/proto/tendermint/version" + "gotest.tools/v3/assert" +) + +func TestListenBeginBlock(t *testing.T) { + t.Run("TestListenBeginBlock", func(t *testing.T) { + + logger := log.TestingLogger() + header := tmprototypes.Header{} + ischeck := true + ctx := sdk.NewContext(nil, header, ischeck, logger) + sdkctx := sdk.UnwrapSDKContext(ctx) + + getTStoreFunc := func(ctx sdk.Context) sdk.KVStore { + return sdkctx.TransientStore(sdk.NewKVStoreKey("somekey")) + } + + ss := NewStreamingService(logger.With("module", "mamoru"), nil, getTStoreFunc) + + req := tmabci.RequestBeginBlock{Header: tmprototypes.Header{ + Version: tmversion.Consensus{}, + ChainID: "", + Height: 1234, + Time: time.Time{}, + LastBlockId: tmprototypes.BlockID{}, + LastCommitHash: []byte{'a', 'b', 'c'}, + DataHash: []byte{'a', 'b', 'c'}, + ValidatorsHash: []byte{'a', 'b', 'c'}, + NextValidatorsHash: []byte{'a', 'b', 'c'}, + ConsensusHash: []byte{'a', 'b', 'c'}, + AppHash: []byte{'a', 'b', 'c'}, + LastResultsHash: []byte{'a', 'b', 'c'}, EvidenceHash: []byte{'a', 'b', 'c'}, + ProposerAddress: []byte{'a', 'b', 'c'}, + }} + res := tmabci.ResponseBeginBlock{} + + err := ss.ListenBeginBlock(ctx, req, res) + assert.NilError(t, err) + assert.Equal(t, ss.blockMetadata.RequestBeginBlock.Header.Height, req.Header.Height) + assert.Equal(t, ss.currentBlockNumber, req.Header.Height) + assert.DeepEqual(t, ss.blockMetadata.RequestBeginBlock.Header.LastCommitHash, req.Header.LastCommitHash) + assert.DeepEqual(t, ss.blockMetadata.RequestBeginBlock.Header.DataHash, req.Header.DataHash) + assert.DeepEqual(t, ss.blockMetadata.RequestBeginBlock.Header.ValidatorsHash, req.Header.ValidatorsHash) + assert.DeepEqual(t, ss.blockMetadata.RequestBeginBlock.Header.NextValidatorsHash, req.Header.NextValidatorsHash) + assert.DeepEqual(t, ss.blockMetadata.RequestBeginBlock.Header.ConsensusHash, req.Header.ConsensusHash) + assert.DeepEqual(t, ss.blockMetadata.RequestBeginBlock.Header.AppHash, req.Header.AppHash) + assert.DeepEqual(t, ss.blockMetadata.RequestBeginBlock.Header.LastResultsHash, req.Header.LastResultsHash) + assert.DeepEqual(t, ss.blockMetadata.RequestBeginBlock.Header.EvidenceHash, req.Header.EvidenceHash) + assert.DeepEqual(t, ss.blockMetadata.RequestBeginBlock.Header.ProposerAddress, req.Header.ProposerAddress) + }) +} diff --git a/mamoru_cosmos_sdk/sync_state/client.go b/mamoru_cosmos_sdk/sync_state/client.go new file mode 100644 index 0000000000..aba1678060 --- /dev/null +++ b/mamoru_cosmos_sdk/sync_state/client.go @@ -0,0 +1,198 @@ +package sync_state + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "os/signal" + "strconv" + "syscall" + "time" + + "github.com/tendermint/tendermint/libs/log" +) + +type JSONRPCResponse struct { + Jsonrpc string `json:"jsonrpc"` + ID int `json:"id"` + Result Result `json:"result"` +} + +type Result struct { + NodeInfo NodeInfo `json:"node_info"` + SyncInfo SyncInfo `json:"sync_info"` + ValidatorInfo ValidatorInfo `json:"validator_info"` +} + +type NodeInfo struct { + ProtocolVersion ProtocolVersion `json:"protocol_version"` + ID string `json:"id"` + ListenAddr string `json:"listen_addr"` + Network string `json:"network"` + Version string `json:"version"` + Channels string `json:"channels"` + Moniker string `json:"moniker"` + Other Other `json:"other"` +} + +type ProtocolVersion struct { + P2P string `json:"p2p"` + Block string `json:"block"` + App string `json:"app"` +} + +type Other struct { + TxIndex string `json:"tx_index"` + RPCAddress string `json:"rpc_address"` +} + +type SyncInfo struct { + LatestBlockHash string `json:"latest_block_hash"` + LatestAppHash string `json:"latest_app_hash"` + LatestBlockHeight string `json:"latest_block_height"` + LatestBlockTime time.Time `json:"latest_block_time"` + EarliestBlockHash string `json:"earliest_block_hash"` + EarliestAppHash string `json:"earliest_app_hash"` + EarliestBlockHeight string `json:"earliest_block_height"` + EarliestBlockTime time.Time `json:"earliest_block_time"` + CatchingUp bool `json:"catching_up"` +} + +type ValidatorInfo struct { + Address string `json:"address"` + PubKey PubKey `json:"pub_key"` + VotingPower string `json:"voting_power"` +} + +type PubKey struct { + Type string `json:"type"` + Value string `json:"value"` +} + +func (sync *JSONRPCResponse) GetCurrentBlockNumber() uint64 { + if sync == nil { + return 0 + } + // convert string to uint64 + blockHeight, err := strconv.ParseUint(sync.Result.SyncInfo.LatestBlockHeight, 10, 64) + if err != nil { + return 0 + } + return blockHeight +} + +func (sync *JSONRPCResponse) IsSync() bool { + if sync == nil { + return false + } + + return !sync.Result.SyncInfo.CatchingUp +} + +type Client struct { + logger log.Logger + syncData *JSONRPCResponse + Url string + PolishTimeSec uint + + quit chan struct{} + signals chan os.Signal +} + +func NewHTTPRequest(logget log.Logger, url string, PolishTimeSec uint, enable bool) *Client { + c := &Client{ + logger: logget, + Url: url, + PolishTimeSec: PolishTimeSec, + quit: make(chan struct{}), + signals: make(chan os.Signal, 1), + } + + // Register for SIGINT (Ctrl+C) and SIGTERM (kill) signals + signal.Notify(c.signals, syscall.SIGINT, syscall.SIGTERM) + if enable { + go c.loop() + } + + return c +} + +func (c *Client) GetSyncData() *JSONRPCResponse { + return c.syncData +} + +func (c *Client) loop() { + // wait for 2 minutes for the node to start + time.Sleep(2 * time.Minute) + ticker := time.NewTicker(time.Duration(c.PolishTimeSec) * time.Second) + defer ticker.Stop() + // Perform the first tick immediately + c.fetchSyncStatus() + + for { + select { + case <-ticker.C: + c.fetchSyncStatus() + case <-c.quit: + c.logger.Info("Mamoru SyncProcess Shutting down...") + return + case <-c.signals: + c.logger.Info("Signal received, initiating shutdown...") + c.Close() + } + } +} + +func (c *Client) fetchSyncStatus() { + + // Create a context with timeout + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + c.logger.Info("Mamoru requesting syncData ...") + // Send the request and get the response + response, err := sendJSONRPCRequest(ctx, c.Url) + if err != nil { + c.logger.Error("Mamoru Sync", "error", err) + return + } + c.logger.Info("Mamoru Sync", "response", response != nil) + c.syncData = response +} + +func (c *Client) Close() { + close(c.quit) +} + +func sendJSONRPCRequest(ctx context.Context, url string) (*JSONRPCResponse, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, err + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP request returned status code %d", resp.StatusCode) + } + + responseBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var response JSONRPCResponse + err = json.Unmarshal(responseBody, &response) + if err != nil { + return nil, err + } + + return &response, nil +} diff --git a/mamoru_cosmos_sdk/sync_state/client_test.go b/mamoru_cosmos_sdk/sync_state/client_test.go new file mode 100644 index 0000000000..ff537adb86 --- /dev/null +++ b/mamoru_cosmos_sdk/sync_state/client_test.go @@ -0,0 +1,146 @@ +package sync_state + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/libs/log" +) + +var mockSyncStatusResponse = `{ + "jsonrpc": "2.0", + "id": -1, + "result": { + "node_info": { + "protocol_version": { + "p2p": "8", + "block": "11", + "app": "0" + }, + "id": "adde11e52e960a6b204ec0fdfbfbf65049d325bf", + "listen_addr": "tcp://0.0.0.0:26655", + "network": "kava_2222-10", + "version": "0.34.27", + "channels": "40202122233038606100", + "moniker": "mamoru-kava", + "other": { + "tx_index": "on", + "rpc_address": "tcp://127.0.0.1:26658" + } + }, + "sync_info": { + "latest_block_hash": "320B1CBF4D15D5E5BA89E3D36121385874251C3BD1847C2D6CE47BFCFD4F4D09", + "latest_app_hash": "FA397677181078430BEC3E10F829111D554963A94618DDCC2B3DAD73F3FFA54D", + "latest_block_height": "9042629", + "latest_block_time": "2024-03-18T10:54:16.728631892Z", + "earliest_block_hash": "72CD24385249F6BF6F1ECD92E9B9EDA6A5DD241D74A7501EC818BCE132D32E0F", + "earliest_app_hash": "785C3EBA43E200377C68E907ADD843EC529B55A931F39C38B9035D34E1C6A1F0", + "earliest_block_height": "9037588", + "earliest_block_time": "2024-03-18T02:08:51.032328852Z", + "catching_up": true + }, + "validator_info": { + "address": "60321514488E8840E437FE6E991D76ABFB15C5A7", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "BFVaUzOk0pmh4hIilFF+9A20fgtUq3o5ngyAUgcWanc=" + }, + "voting_power": "0" + } + } +}` + +func TestNewHTTPRequest(t *testing.T) { + logger := log.NewNopLogger() + client := NewHTTPRequest(logger, "http://localhost", 10, true) + assert.NotNil(t, client) +} + +func TestGetCurrentBlockNumber(t *testing.T) { + sync := &JSONRPCResponse{ + Result: Result{ + SyncInfo: SyncInfo{ + LatestBlockHeight: "100", + }, + }, + } + assert.Equal(t, uint64(100), sync.GetCurrentBlockNumber()) +} + +func TestGetCurrentBlockNumberWithInvalidHeight(t *testing.T) { + sync := &JSONRPCResponse{ + Result: Result{ + SyncInfo: SyncInfo{ + LatestBlockHeight: "invalid", + }, + }, + } + assert.Equal(t, uint64(0), sync.GetCurrentBlockNumber()) +} + +func TestIsSync(t *testing.T) { + sync := &JSONRPCResponse{ + Result: Result{ + SyncInfo: SyncInfo{ + CatchingUp: false, + }, + }, + } + assert.True(t, sync.IsSync()) +} + +func TestSendJSONRPCRequestSuccess(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + _, _ = rw.Write([]byte(mockSyncStatusResponse)) + })) + defer server.Close() + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + response, err := sendJSONRPCRequest(ctx, server.URL) + assert.NoError(t, err) + assert.NotNil(t, response) +} + +func TestSendJSONRPCRequestFailure(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + _, err := sendJSONRPCRequest(ctx, "http://invalid-url") + assert.Error(t, err) +} + +func TestFetchSyncStatus(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + _, _ = rw.Write([]byte(mockSyncStatusResponse)) + })) + defer server.Close() + + logger := log.NewNopLogger() + client := NewHTTPRequest(logger, server.URL, 10, true) + client.fetchSyncStatus() + + assert.NotNil(t, client.GetSyncData()) +} + +func TestFetchSyncStatusWithInvalidURL(t *testing.T) { + logger := log.NewNopLogger() + client := NewHTTPRequest(logger, "http://invalid-url", 10, true) + client.fetchSyncStatus() + + assert.Nil(t, client.GetSyncData()) +} + +func TestClose(t *testing.T) { + logger := log.NewNopLogger() + client := NewHTTPRequest(logger, "http://localhost", 10, true) + client.Close() + + _, ok := <-client.quit + assert.False(t, ok) +}