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

chore(erc20): add erc20 extension approval tests #2005

Merged
merged 25 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b1bbaba
Add approve tests
MalteHerrmann Nov 6, 2023
1b43357
remove unnecessary gas field from testcases
MalteHerrmann Nov 6, 2023
31d138b
fix tests / precompile behavior
MalteHerrmann Nov 6, 2023
30d925e
add authz client to network utils
MalteHerrmann Nov 6, 2023
ea6e979
store bondDenom in test suite
MalteHerrmann Nov 6, 2023
c331961
commit WIP in authz tests
MalteHerrmann Nov 6, 2023
8ef22ea
run make format
MalteHerrmann Nov 6, 2023
c04bea2
fix unpacking authorization from response
MalteHerrmann Nov 6, 2023
988efd9
add authz getters to integration test utils
MalteHerrmann Nov 6, 2023
e7d7a6f
refactor existing tests with grpcHandler authz getters
MalteHerrmann Nov 6, 2023
839141d
nit empty line for license
MalteHerrmann Nov 6, 2023
b3f3afb
extract testing utilities
MalteHerrmann Nov 6, 2023
fae8e60
format
MalteHerrmann Nov 6, 2023
e00c747
run make format
MalteHerrmann Nov 6, 2023
8aa2ca0
adjust GetAllowance to avoid extra read from store
MalteHerrmann Nov 6, 2023
86844dc
add increase and decrease allowance tests
MalteHerrmann Nov 6, 2023
cc6e572
run make format
MalteHerrmann Nov 6, 2023
98ab043
add changelog entry
MalteHerrmann Nov 6, 2023
a71ddee
address linters
MalteHerrmann Nov 6, 2023
9973551
Merge branch 'main' into malte/erc20-extension-approve-tests
MalteHerrmann Nov 6, 2023
c27f796
add nosec escapes for error handling
MalteHerrmann Nov 7, 2023
508fc5c
Update precompiles/erc20/approve.go
MalteHerrmann Nov 7, 2023
3354ac2
Merge branch 'main' into malte/erc20-extension-approve-tests
MalteHerrmann Nov 7, 2023
9bc3b45
update gomod2nix.toml file
MalteHerrmann Nov 7, 2023
6b6253a
Merge branch 'main' into malte/erc20-extension-approve-tests
MalteHerrmann Nov 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 1 addition & 5 deletions precompiles/erc20/approve.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@
granter := contract.CallerAddress

// TODO: owner should be the owner of the contract
authorization, _, err := auth.CheckAuthzExists(ctx, p.AuthzKeeper, grantee, granter, SendMsgURL)
if err != nil {
return nil, err
}
// case 1: authorization doesn't exist
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
authorization, _, _ := auth.CheckAuthzExists(ctx, p.AuthzKeeper, grantee, granter, SendMsgURL)
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show resolved Hide resolved

switch {
case authorization == nil && amount != nil && amount.Cmp(common.Big0) <= 0:
Expand Down
184 changes: 184 additions & 0 deletions precompiles/erc20/approve_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package erc20_test

import (
"math/big"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/authz"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/evmos/evmos/v15/app"
"github.com/evmos/evmos/v15/encoding"
"github.com/evmos/evmos/v15/precompiles/authorization"
"github.com/evmos/evmos/v15/precompiles/testutil"
commonfactory "github.com/evmos/evmos/v15/testutil/integration/common/factory"
)

func (s *PrecompileTestSuite) TestApprove() {
method := s.precompile.Methods[authorization.ApproveMethod]
amount := int64(100)

testcases := []struct {
name string
malleate func() []interface{}
postCheck func()
expPass bool
errContains string
}{
{
name: "fail - empty args",
malleate: func() []interface{} { return nil },
errContains: "invalid number of arguments",
},
{
name: "fail - invalid number of arguments",
malleate: func() []interface{} {
return []interface{}{
1, 2, 3,
}
},
errContains: "invalid number of arguments",
},
{
name: "fail - invalid address",
malleate: func() []interface{} {
return []interface{}{
"invalid address", big.NewInt(2),
}
},
errContains: "invalid address",
},
{
name: "fail - invalid amount",
malleate: func() []interface{} {
return []interface{}{
s.keyring.GetAddr(1), "invalid amount",
}
},
errContains: "invalid amount",
},
{
name: "fail - negative amount",
malleate: func() []interface{} {
return []interface{}{
s.keyring.GetAddr(1), big.NewInt(-1),
}
},
errContains: "cannot approve non-positive values",
},
{
name: "pass - approve without existing authorization",
malleate: func() []interface{} {
return []interface{}{
s.keyring.GetAddr(1), big.NewInt(amount),
}
},
expPass: true,
postCheck: func() {
// Get approvals from AuthzKeeper
approvals, err := s.network.App.AuthzKeeper.GetAuthorizations(
s.network.GetContext(),
s.keyring.GetAccAddr(1),
s.keyring.GetAccAddr(0),
)
s.Require().NoError(err, "expected no error")
s.Require().Len(approvals, 1, "expected one approval")
_, ok := approvals[0].(*banktypes.SendAuthorization)
s.Require().True(ok, "expected send authorization")
},
},
{
name: "pass - approve with existing authorization",
malleate: func() []interface{} {
// TODO: refactor into integration test suite
sendAuthz := banktypes.NewSendAuthorization(
sdk.NewCoins(sdk.NewInt64Coin(s.bondDenom, 1)),
[]sdk.AccAddress{},
)

expiration := s.network.GetContext().BlockHeader().Time.Add(time.Hour)

msgGrant, err := authz.NewMsgGrant(
s.keyring.GetAccAddr(0),
s.keyring.GetAccAddr(1),
sendAuthz,
&expiration,
)
s.Require().NoError(err, "expected no error creating the MsgGrant")

// Create an authorization
txArgs := commonfactory.CosmosTxArgs{Msgs: []sdk.Msg{msgGrant}}
_, err = s.factory.ExecuteCosmosTx(s.keyring.GetPrivKey(0), txArgs)
s.Require().NoError(err, "expected no error executing the MsgGrant tx")

return []interface{}{
s.keyring.GetAddr(1), big.NewInt(2 * amount),
}
},
expPass: true,
postCheck: func() {
// Get approvals from Authz client
authzClient := s.network.GetAuthzClient()
req := &authz.QueryGranteeGrantsRequest{Grantee: s.keyring.GetAccAddr(1).String()}
res, err := authzClient.GranteeGrants(s.network.GetContext(), req)
s.Require().NoError(err, "expected no error querying the grants")
s.Require().Len(res.Grants, 1, "expected one grant")

encodingCfg := encoding.MakeConfig(app.ModuleBasics)
var authz banktypes.SendAuthorization
// FIXME: how to unpack types.Any here?
err = encodingCfg.Codec.UnpackAny(res.Grants[0].Authorization, &authz)
// err = encodingCfg.InterfaceRegistry.UnpackAny(res.Grants[0].Authorization, &authz)
s.Require().NoError(err, "expected no error unpacking the authorization")

// Check that the authorization has the correct amount
s.Require().Len(authz.SpendLimit, 1, "expected spend limit in one denomination")
s.Require().Equal(2*amount, authz.SpendLimit[0].Amount.Int64(), "expected correct amount")
},
},
}

for _, tc := range testcases {
s.Run(tc.name, func() {
s.SetupTest()

ctx := s.network.GetContext()

var contract *vm.Contract
contract, ctx = testutil.NewPrecompileContract(
s.T(),
ctx,
s.keyring.GetAddr(0),
s.precompile,
200_000,
)

var args []interface{}
if tc.malleate != nil {
args = tc.malleate()
}

bz, err := s.precompile.Approve(
ctx,
contract,
s.network.GetStateDB(),
&method,
args,
)

if tc.expPass {
s.Require().NoError(err, "expected no error")
s.Require().NotNil(bz, "expected non-nil bytes")
} else {
s.Require().Error(err, "expected error")
s.Require().ErrorContains(err, tc.errContains, "expected different error message")
s.Require().Empty(bz, "expected empty bytes")
}

if tc.postCheck != nil {
tc.postCheck()
}
})
}
}
9 changes: 8 additions & 1 deletion precompiles/erc20/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var s *PrecompileTestSuite
type PrecompileTestSuite struct {
suite.Suite

bondDenom string
network *network.UnitTestNetwork
factory factory.TxFactory
grpcHandler grpc.Handler
Expand All @@ -34,7 +35,7 @@ func TestPrecompileTestSuite(t *testing.T) {
}

func (s *PrecompileTestSuite) SetupTest() {
keyring := testkeyring.New(1)
keyring := testkeyring.New(2)
integrationNetwork := network.NewUnitTestNetwork(
network.WithPreFundedAccounts(keyring.GetAllAccAddrs()...),
)
Expand All @@ -52,6 +53,12 @@ func (s *PrecompileTestSuite) SetupTest() {
)
s.Require().NoError(err, "failed to create erc20 precompile")

ctx := integrationNetwork.GetContext()
sk := integrationNetwork.App.StakingKeeper
bondDenom := sk.BondDenom(ctx)
s.Require().NotEmpty(bondDenom, "bond denom cannot be empty")

s.bondDenom = bondDenom
s.factory = txFactory
s.grpcHandler = grpcHandler
s.keyring = keyring
Expand Down
6 changes: 4 additions & 2 deletions testutil/integration/common/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import (
sdktypes "github.com/cosmos/cosmos-sdk/types"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/authz"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
ibctesting "github.com/cosmos/ibc-go/v7/testing"
)

// Network is the interface that wraps the commong methods to interact with integration test network.
// Network is the interface that wraps the common methods to interact with integration test network.
//
// It was designed to avoid users to access module's keepers directly and force integration tests
// to be closer to the real user's behavior.
Expand All @@ -28,8 +29,9 @@ type Network interface {

// Clients
GetAuthClient() authtypes.QueryClient
GetStakingClient() stakingtypes.QueryClient
GetAuthzClient() authz.QueryClient
GetBankClient() banktypes.QueryClient
GetStakingClient() stakingtypes.QueryClient

BroadcastTxSync(txBytes []byte) (abcitypes.ResponseDeliverTx, error)
Simulate(txBytes []byte) (*txtypes.SimulateResponse, error)
Expand Down
7 changes: 7 additions & 0 deletions testutil/integration/evmos/network/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/cosmos/cosmos-sdk/baseapp"
sdktypes "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/authz"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
Expand Down Expand Up @@ -62,6 +63,12 @@ func (n *IntegrationNetwork) GetAuthClient() authtypes.QueryClient {
return authtypes.NewQueryClient(queryHelper)
}

func (n *IntegrationNetwork) GetAuthzClient() authz.QueryClient {
queryHelper := getQueryHelper(n.GetContext())
authz.RegisterQueryServer(queryHelper, n.app.AuthzKeeper)
return authz.NewQueryClient(queryHelper)
}

func (n *IntegrationNetwork) GetStakingClient() stakingtypes.QueryClient {
queryHelper := getQueryHelper(n.GetContext())
stakingtypes.RegisterQueryServer(queryHelper, stakingkeeper.Querier{Keeper: &n.app.StakingKeeper})
Expand Down