Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add simulation app, implement e2e tests #1

Merged
merged 6 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: End to End Tests

on:
pull_request:

johnletey marked this conversation as resolved.
Show resolved Hide resolved
env:
TAR_PATH: docker-image.tar
ARTIFACT_NAME: tar-docker-image

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Build Docker image
uses: strangelove-ventures/[email protected]
with:
registry: "" # empty registry, image only shared for e2e testing
tag: local # emulate local environment for consistency in interchaintest cases
tar-export-path: ${{ env.TAR_PATH }} # export a tarball that can be uploaded as an artifact for the e2e jobs
platform: linux/amd64 # test runner architecture only
git-ref: ${{ github.head_ref }} # source code ref

# Heighliner chains.yaml config
chain: noble-globalfee-simd
dockerfile: cosmos
build-target: make build
build-dir: simapp
binaries: |
- simapp/build/simd

- name: Publish Tarball as Artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.TAR_PATH }}

prepare:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Install Go
uses: actions/setup-go@v5
with:
go-version: '1.22'

- name: Generate Matrix
id: set-matrix
run: |
# Run the command and convert its output to a JSON array
TESTS=$(cd e2e && go test -list . | grep -v "^ok " | jq -R -s -c 'split("\n")[:-1]')
johnletey marked this conversation as resolved.
Show resolved Hide resolved
echo "matrix=${TESTS}" >> $GITHUB_OUTPUT

test:
needs:
- build
- prepare
runs-on: ubuntu-latest
strategy:
matrix:
# names of `make` commands to run tests
test: ${{fromJson(needs.prepare.outputs.matrix)}}
fail-fast: false

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Install Go
uses: actions/setup-go@v5
with:
go-version: '1.22'

- name: Download Tarball Artifact
uses: actions/download-artifact@v4
with:
name: ${{ env.ARTIFACT_NAME }}

- name: Load Docker Image
run: docker image load -i ${{ env.TAR_PATH }}

- name: run test
run: cd e2e && go test -race -v -timeout 15m -run ^${{ matrix.test }}$ .
johnletey marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.idea
coverage.out
build/
debug_container.*
23 changes: 21 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
.PHONY: proto-format proto-lint proto-gen license format lint test-unit
all: proto-all format lint test-unit
.PHONY: proto-format proto-lint proto-gen license format lint test-unit build local-image test-e2e
all: proto-all format lint test-unit build local-image test-e2e

###############################################################################
### Build ###
###############################################################################

build:
@echo "🤖 Building simd..."
@cd simapp && make build 1> /dev/null
@echo "✅ Completed build!"

###############################################################################
### Tooling ###
Expand Down Expand Up @@ -53,7 +62,17 @@ proto-lint:
### Testing ###
###############################################################################

local-image:
@echo "🤖 Building image..."
@heighliner build --file ./e2e/chains.yaml --chain noble-globalfee-simd --local
@echo "✅ Completed build!"

test-unit:
@echo "🤖 Running unit tests..."
@go test -cover -coverprofile=coverage.out -race -v ./keeper/...
@echo "✅ Completed unit tests!"

test-e2e:
@echo "🤖 Running e2e tests..."
@cd e2e && go test -timeout 15m -race -v ./...
@echo "✅ Completed e2e tests!"
johnletey marked this conversation as resolved.
Show resolved Hide resolved
50 changes: 26 additions & 24 deletions ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,36 +32,38 @@ func TxFeeChecker(keeper *keeper.Keeper) ante.TxFeeChecker {
if !ok {
return nil, 0, errors.Wrap(errorstypes.ErrTxDecode, "Tx must be a FeeTx")
}
fees := feeTx.GetFee()

allBypassMessages := true
for _, msg := range feeTx.GetMsgs() {
if has, _ := keeper.BypassMessages.Has(ctx, sdk.MsgTypeURL(msg)); !has {
allBypassMessages = false
if ctx.IsCheckTx() {
allBypassMessages := true
for _, msg := range feeTx.GetMsgs() {
if has, _ := keeper.BypassMessages.Has(ctx, sdk.MsgTypeURL(msg)); !has {
allBypassMessages = false
}
}
johnletey marked this conversation as resolved.
Show resolved Hide resolved
if allBypassMessages {
return sdk.Coins{}, 0, nil
}
}
if allBypassMessages {
return sdk.Coins{}, 0, nil
}

requiredFees, err := keeper.GetRequiredFees(ctx, feeTx)
if err != nil {
return nil, 0, err
}
if len(requiredFees) == 0 {
return sdk.Coins{}, 0, nil
}
requiredFees, err := keeper.GetRequiredFees(ctx, feeTx)
if err != nil {
return nil, 0, err
}
if len(requiredFees) == 0 {
return sdk.Coins{}, 0, nil
}

fees := feeTx.GetFee()
sufficientFees := false
for _, fee := range fees {
found, requiredFee := requiredFees.Find(fee.Denom)
if found && fee.Amount.GTE(requiredFee.Amount) {
sufficientFees = true
sufficientFees := false
for _, fee := range fees {
found, requiredFee := requiredFees.Find(fee.Denom)
if found && fee.Amount.GTE(requiredFee.Amount) {
sufficientFees = true
}
}
}

if !sufficientFees {
return nil, 0, errors.Wrapf(errorstypes.ErrInsufficientFee, "expected at least one of %s", requiredFees)
if !sufficientFees {
return nil, 0, errors.Wrapf(errorstypes.ErrInsufficientFee, "expected at least one of %s", requiredFees)
}
johnletey marked this conversation as resolved.
Show resolved Hide resolved
}

return fees, getTxPriority(fees, int64(feeTx.GetGas())), nil
Expand Down
76 changes: 76 additions & 0 deletions client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2024 NASD Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cli

import (
"fmt"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/noble-assets/globalfee/types"
"github.com/spf13/cobra"
)

func GetTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: types.ModuleName,
Short: fmt.Sprintf("Transactions commands for the %s module", types.ModuleName),
DisableFlagParsing: false,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}

cmd.AddCommand(TxUpdateGasPrices())

return cmd
}

func TxUpdateGasPrices() *cobra.Command {
cmd := &cobra.Command{
Use: "update-gas-prices [gas-prices ...]",
Example: "update-gas-prices ustake ulove",
Short: "Execute the UpdateGasPrices RPC method",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
johnletey marked this conversation as resolved.
Show resolved Hide resolved
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

var gasPrices sdk.DecCoins
for _, arg := range args {
gasPrice, err := sdk.ParseDecCoin(arg)
if err != nil {
return err
}
johnletey marked this conversation as resolved.
Show resolved Hide resolved

gasPrices = append(gasPrices, gasPrice)
}
johnletey marked this conversation as resolved.
Show resolved Hide resolved

msg := &types.MsgUpdateGasPrices{
Signer: clientCtx.GetFromAddress().String(),
GasPrices: gasPrices,
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
johnletey marked this conversation as resolved.
Show resolved Hide resolved
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
6 changes: 6 additions & 0 deletions e2e/chains.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- name: noble-globalfee-simd
dockerfile: cosmos
build-dir: simapp
build-target: make build
binaries:
- simapp/build/simd
johnletey marked this conversation as resolved.
Show resolved Hide resolved
81 changes: 81 additions & 0 deletions e2e/globalfee_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright 2024 NASD Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package e2e

import (
"encoding/json"
"fmt"
"testing"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/noble-assets/globalfee/types"
"github.com/strangelove-ventures/interchaintest/v8"
"github.com/stretchr/testify/require"
)

func TestGlobalFee(t *testing.T) {
ctx, wrapper := GlobalFeeSuite(t)
validator := wrapper.Chain.Validators[0]

var (
gasPrice = sdk.DecCoin{Denom: "ustake", Amount: math.LegacyNewDec(10)}
gas = math.NewInt(200_000) // default gas
fee = fmt.Sprintf("%sustake", gasPrice.Amount.MulInt(gas).String())
)

// ARRANGE: Generate a sender and recipient wallet.
sender := interchaintest.GetAndFundTestUsers(t, ctx, "sender", math.NewInt(100_000_000_000), wrapper.Chain)[0]
recipient, err := wrapper.Chain.BuildRelayerWallet(ctx, "recipient")
require.NoError(t, err)

// ACT: Attempt a transaction with no fees.
err = bankSendWithFees(ctx, validator, sender, recipient, "1ustake", "0ustake")
// ASSERT: The transaction was successful due to no required fees.
require.NoError(t, err)
// ACT: Set required gas prices to 10ustake.
_, err = validator.ExecTx(ctx, wrapper.Authority.KeyName(), "globalfee", "update-gas-prices", gasPrice.String())
// ASSERT: The transaction was successful and updated the required gas prices.
require.NoError(t, err)
gasPrices, err := GasPrices(ctx, validator)
require.NoError(t, err)
require.Equal(t, sdk.NewDecCoins(gasPrice), gasPrices)

// ACT: Attempt a transaction with no fees.
err = bankSendWithFees(ctx, validator, sender, recipient, "1ustake", "0ustake")
// ASSERT: The transaction failed due to insufficient fees.
require.ErrorContains(t, err, "insufficient fee")

// ACT: Add bank transfer messages to bypass fee
bankSendType := sdk.MsgTypeURL(&banktypes.MsgSend{}) // /cosmos.bank.v1beta1.MsgSend
_, err = validator.ExecTx(ctx, wrapper.Authority.KeyName(), "globalfee", "update-bypass-messages", bankSendType, "--fees", fee)
require.NoError(t, err)
// ASSERT: The transaction successfully updated the bypass messages
raw, _, err := validator.ExecQuery(ctx, "globalfee", "bypass-messages")
require.NoError(t, err)
var bypassMessagesResponse types.QueryBypassMessagesResponse
err = json.Unmarshal(raw, &bypassMessagesResponse)
require.NoError(t, err)
expectedBypassMessages := types.QueryBypassMessagesResponse{
BypassMessages: []string{bankSendType},
}
require.Equal(t, expectedBypassMessages, bypassMessagesResponse)

// ACT: Attempt a bank send with no fees now that that message types is bypassed
err = bankSendWithFees(ctx, validator, sender, recipient, "1ustake", "0ustake")
// ASSERT: The transaction was successful with no fees.
require.NoError(t, err)
}
johnletey marked this conversation as resolved.
Show resolved Hide resolved
Loading