wasmd/tests/e2e/grants_test.go

342 lines
12 KiB
Go

package e2e_test
import (
"fmt"
"os"
"testing"
"time"
wasmvm "github.com/CosmWasm/wasmvm/v3"
ibctesting "github.com/cosmos/ibc-go/v10/testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/authz"
"github.com/CosmWasm/wasmd/tests/e2e"
wasmibctesting "github.com/CosmWasm/wasmd/tests/wasmibctesting"
"github.com/CosmWasm/wasmd/x/wasm/types"
)
func TestGrants(t *testing.T) {
// Given a contract by address A
// And a grant for address B by A created
// When B sends an execute with tokens from A
// Then the grant is executed as defined
// And
// - balance A reduced (on success)
// - balance B not touched
coord := wasmibctesting.NewCoordinator(t, 1)
chain := wasmibctesting.NewWasmTestChain(coord.GetChain(ibctesting.GetChainID(1)))
contractAddr := e2e.InstantiateReflectContract(t, chain)
require.NotEmpty(t, contractAddr)
granterAddr := chain.SenderAccount.GetAddress()
granteePrivKey := secp256k1.GenPrivKey()
granteeAddr := sdk.AccAddress(granteePrivKey.PubKey().Address().Bytes())
otherPrivKey := secp256k1.GenPrivKey()
otherAddr := sdk.AccAddress(otherPrivKey.PubKey().Address().Bytes())
chain.Fund(granteeAddr, sdkmath.NewInt(1_000_000))
chain.Fund(otherAddr, sdkmath.NewInt(1_000_000))
assert.Equal(t, sdkmath.NewInt(1_000_000), chain.Balance(granteeAddr, sdk.DefaultBondDenom).Amount)
myAmount := sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(2_000_000))
specs := map[string]struct {
limit types.ContractAuthzLimitX
filter types.ContractAuthzFilterX
transferAmount sdk.Coin
senderKey cryptotypes.PrivKey
expErr *errorsmod.Error
}{
"in limits and filter": {
limit: types.NewMaxFundsLimit(myAmount),
filter: types.NewAllowAllMessagesFilter(),
transferAmount: myAmount,
senderKey: granteePrivKey,
},
"exceed limits": {
limit: types.NewMaxFundsLimit(myAmount),
filter: types.NewAllowAllMessagesFilter(),
transferAmount: myAmount.Add(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.OneInt())),
senderKey: granteePrivKey,
expErr: sdkerrors.ErrUnauthorized,
},
"not match filter": {
limit: types.NewMaxFundsLimit(myAmount),
filter: types.NewAcceptedMessageKeysFilter("foo"),
transferAmount: sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.OneInt()),
senderKey: granteePrivKey,
expErr: sdkerrors.ErrUnauthorized,
},
"non authorized sender address": { // sanity check - testing sdk
limit: types.NewMaxFundsLimit(myAmount),
filter: types.NewAllowAllMessagesFilter(),
senderKey: otherPrivKey,
transferAmount: myAmount,
expErr: authz.ErrNoAuthorizationFound,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
// setup grant
grant, err := types.NewContractGrant(contractAddr, spec.limit, spec.filter)
require.NoError(t, err)
authorization := types.NewContractExecutionAuthorization(*grant)
expiry := time.Now().Add(time.Hour)
grantMsg, err := authz.NewMsgGrant(granterAddr, granteeAddr, authorization, &expiry)
require.NoError(t, err)
_, err = chain.SendMsgs(grantMsg)
require.NoError(t, err)
granterStartBalance := chain.Balance(granterAddr, sdk.DefaultBondDenom).Amount
// when
anyValidReflectMsg := []byte(fmt.Sprintf(`{"reflect_msg": {"msgs": [{"bank":{"burn":{"amount":[{"denom":%q, "amount": %q}]}}}]}}`, sdk.DefaultBondDenom, myAmount.Amount.String()))
execMsg := authz.NewMsgExec(spec.senderKey.PubKey().Address().Bytes(), []sdk.Msg{&types.MsgExecuteContract{
Sender: granterAddr.String(),
Contract: contractAddr.String(),
Msg: anyValidReflectMsg,
Funds: sdk.NewCoins(spec.transferAmount),
}})
_, gotErr := chain.SendNonDefaultSenderMsgs(spec.senderKey, &execMsg)
// then
if spec.expErr != nil {
require.ErrorContains(t, gotErr, fmt.Sprintf("%s/%d:", spec.expErr.Codespace(), spec.expErr.ABCICode()))
assert.Equal(t, sdkmath.NewInt(1_000_000), chain.Balance(granteeAddr, sdk.DefaultBondDenom).Amount)
assert.Equal(t, granterStartBalance, chain.Balance(granterAddr, sdk.DefaultBondDenom).Amount)
return
}
require.NoError(t, gotErr)
assert.Equal(t, sdkmath.NewInt(1_000_000), chain.Balance(granteeAddr, sdk.DefaultBondDenom).Amount)
assert.Equal(t, granterStartBalance.Sub(spec.transferAmount.Amount), chain.Balance(granterAddr, sdk.DefaultBondDenom).Amount)
})
}
}
func TestStoreCodeGrant(t *testing.T) {
reflectWasmCode, err := os.ReadFile("../../x/wasm/keeper/testdata/reflect_1_1.wasm")
require.NoError(t, err)
reflectCodeChecksum, err := wasmvm.CreateChecksum(reflectWasmCode)
require.NoError(t, err)
coord := wasmibctesting.NewCoordinator(t, 1)
chain := wasmibctesting.NewWasmTestChain(coord.GetChain(ibctesting.GetChainID(1)))
granterAddr := chain.SenderAccount.GetAddress()
granteePrivKey := secp256k1.GenPrivKey()
granteeAddr := sdk.AccAddress(granteePrivKey.PubKey().Address().Bytes())
otherPrivKey := secp256k1.GenPrivKey()
otherAddr := sdk.AccAddress(otherPrivKey.PubKey().Address().Bytes())
chain.Fund(granteeAddr, sdkmath.NewInt(1_000_000))
chain.Fund(otherAddr, sdkmath.NewInt(1_000_000))
assert.Equal(t, sdkmath.NewInt(1_000_000), chain.Balance(granteeAddr, sdk.DefaultBondDenom).Amount)
specs := map[string]struct {
codeHash []byte
instantiatePermission types.AccessConfig
senderKey cryptotypes.PrivKey
expErr *errorsmod.Error
}{
"any code hash": {
codeHash: []byte("*"),
instantiatePermission: types.AllowEverybody,
senderKey: granteePrivKey,
},
"match code hash and permission": {
codeHash: reflectCodeChecksum,
instantiatePermission: types.AllowEverybody,
senderKey: granteePrivKey,
},
"not match code hash": {
codeHash: []byte("any_valid_checksum"),
instantiatePermission: types.AllowEverybody,
senderKey: granteePrivKey,
expErr: sdkerrors.ErrUnauthorized,
},
"not match permission": {
codeHash: []byte("*"),
instantiatePermission: types.AllowNobody,
senderKey: granteePrivKey,
expErr: sdkerrors.ErrUnauthorized,
},
"non authorized sender address": {
codeHash: []byte("*"),
instantiatePermission: types.AllowEverybody,
senderKey: otherPrivKey,
expErr: authz.ErrNoAuthorizationFound,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
// setup grant
grant, err := types.NewCodeGrant(spec.codeHash, &spec.instantiatePermission) //nolint:gosec
require.NoError(t, err)
authorization := types.NewStoreCodeAuthorization(*grant)
expiry := time.Now().Add(time.Hour)
grantMsg, err := authz.NewMsgGrant(granterAddr, granteeAddr, authorization, &expiry)
require.NoError(t, err)
_, err = chain.SendMsgs(grantMsg)
require.NoError(t, err)
// when
execMsg := authz.NewMsgExec(spec.senderKey.PubKey().Address().Bytes(), []sdk.Msg{&types.MsgStoreCode{
Sender: granterAddr.String(),
WASMByteCode: reflectWasmCode,
InstantiatePermission: &types.AllowEverybody,
}})
_, gotErr := chain.SendNonDefaultSenderMsgs(spec.senderKey, &execMsg)
// then
if spec.expErr != nil {
assert.ErrorContains(t, gotErr, fmt.Sprintf("%s/%d:", spec.expErr.Codespace(), spec.expErr.ABCICode()))
return
}
require.NoError(t, gotErr)
})
}
}
func TestGzipStoreCodeGrant(t *testing.T) {
hackatomWasmCode, err := os.ReadFile("../../x/wasm/keeper/testdata/hackatom.wasm")
require.NoError(t, err)
hackatomGzipWasmCode, err := os.ReadFile("../../x/wasm/keeper/testdata/hackatom.wasm.gzip")
require.NoError(t, err)
hackatomCodeChecksum, err := wasmvm.CreateChecksum(hackatomWasmCode)
require.NoError(t, err)
coord := wasmibctesting.NewCoordinator(t, 1)
chain := wasmibctesting.NewWasmTestChain(coord.GetChain(ibctesting.GetChainID(1)))
granterAddr := chain.SenderAccount.GetAddress()
granteePrivKey := secp256k1.GenPrivKey()
granteeAddr := sdk.AccAddress(granteePrivKey.PubKey().Address().Bytes())
otherPrivKey := secp256k1.GenPrivKey()
otherAddr := sdk.AccAddress(otherPrivKey.PubKey().Address().Bytes())
chain.Fund(granteeAddr, sdkmath.NewInt(1_000_000))
chain.Fund(otherAddr, sdkmath.NewInt(1_000_000))
assert.Equal(t, sdkmath.NewInt(1_000_000), chain.Balance(granteeAddr, sdk.DefaultBondDenom).Amount)
specs := map[string]struct {
codeHash []byte
instantiatePermission types.AccessConfig
senderKey cryptotypes.PrivKey
expErr *errorsmod.Error
}{
"any code hash": {
codeHash: []byte("*"),
instantiatePermission: types.AllowEverybody,
senderKey: granteePrivKey,
},
"match code hash and permission": {
codeHash: hackatomCodeChecksum,
instantiatePermission: types.AllowEverybody,
senderKey: granteePrivKey,
},
"not match code hash": {
codeHash: []byte("any_valid_checksum"),
instantiatePermission: types.AllowEverybody,
senderKey: granteePrivKey,
expErr: sdkerrors.ErrUnauthorized,
},
"not match permission": {
codeHash: []byte("*"),
instantiatePermission: types.AllowNobody,
senderKey: granteePrivKey,
expErr: sdkerrors.ErrUnauthorized,
},
"non authorized sender address": {
codeHash: []byte("*"),
instantiatePermission: types.AllowEverybody,
senderKey: otherPrivKey,
expErr: authz.ErrNoAuthorizationFound,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
// setup grant
grant, err := types.NewCodeGrant(spec.codeHash, &spec.instantiatePermission) //nolint:gosec
require.NoError(t, err)
authorization := types.NewStoreCodeAuthorization(*grant)
expiry := time.Now().Add(time.Hour)
grantMsg, err := authz.NewMsgGrant(granterAddr, granteeAddr, authorization, &expiry)
require.NoError(t, err)
_, err = chain.SendMsgs(grantMsg)
require.NoError(t, err)
// when
execMsg := authz.NewMsgExec(spec.senderKey.PubKey().Address().Bytes(), []sdk.Msg{&types.MsgStoreCode{
Sender: granterAddr.String(),
WASMByteCode: hackatomGzipWasmCode,
InstantiatePermission: &types.AllowEverybody,
}})
_, gotErr := chain.SendNonDefaultSenderMsgs(spec.senderKey, &execMsg)
// then
if spec.expErr != nil {
assert.ErrorContains(t, gotErr, fmt.Sprintf("%s/%d:", spec.expErr.Codespace(), spec.expErr.ABCICode()))
return
}
require.NoError(t, gotErr)
})
}
}
func TestBrokenGzipStoreCodeGrant(t *testing.T) {
brokenGzipWasmCode, err := os.ReadFile("../../x/wasm/keeper/testdata/broken_crc.gzip")
require.NoError(t, err)
coord := wasmibctesting.NewCoordinator(t, 1)
chain := wasmibctesting.NewWasmTestChain(coord.GetChain(ibctesting.GetChainID(1)))
granterAddr := chain.SenderAccount.GetAddress()
granteePrivKey := secp256k1.GenPrivKey()
granteeAddr := sdk.AccAddress(granteePrivKey.PubKey().Address().Bytes())
otherPrivKey := secp256k1.GenPrivKey()
otherAddr := sdk.AccAddress(otherPrivKey.PubKey().Address().Bytes())
chain.Fund(granteeAddr, sdkmath.NewInt(1_000_000))
chain.Fund(otherAddr, sdkmath.NewInt(1_000_000))
assert.Equal(t, sdkmath.NewInt(1_000_000), chain.Balance(granteeAddr, sdk.DefaultBondDenom).Amount)
codeHash := []byte("*")
instantiatePermission := types.AllowEverybody
senderKey := granteePrivKey
// setup grant
grant, err := types.NewCodeGrant(codeHash, &instantiatePermission)
require.NoError(t, err)
authorization := types.NewStoreCodeAuthorization(*grant)
expiry := time.Now().Add(time.Hour)
grantMsg, err := authz.NewMsgGrant(granterAddr, granteeAddr, authorization, &expiry)
require.NoError(t, err)
_, err = chain.SendMsgs(grantMsg)
require.NoError(t, err)
// when
execMsg := authz.NewMsgExec(senderKey.PubKey().Address().Bytes(), []sdk.Msg{&types.MsgStoreCode{
Sender: granterAddr.String(),
WASMByteCode: brokenGzipWasmCode,
InstantiatePermission: &types.AllowEverybody,
}})
_, gotErr := chain.SendNonDefaultSenderMsgs(senderKey, &execMsg)
// then
require.Error(t, gotErr)
}