refactor(x/authz): set environment in context (#20502)

This commit is contained in:
Julien Robert 2024-06-03 09:48:33 +02:00 committed by GitHub
parent ba4bc19175
commit 0f81966de8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 150 additions and 21 deletions

View File

@ -682,6 +682,14 @@ To learn more see the [docs](https://docs.cosmos.network/main/learn/advanced/tra
### Modules
<!-- create server/v2 changes docs and mention it here
* mention changes in tx validators
* mention changes with appmodulev2
* mention changes with sdk context removal
* mention changes with environment
* mention changes with environment in context in interfaces
-->
#### `**all**`
* [RFC 001](https://docs.cosmos.network/main/rfc/rfc-001-tx-validation) has defined a simplification of the message validation process for modules.

View File

@ -6,3 +6,8 @@ const (
ExecModeKey contextKey = iota
CometInfoKey contextKey = iota
)
// EnvironmentContextKey is the context key for the environment.
// A caller should not assume the environment is available in each context.
// ref: https://github.com/cosmos/cosmos-sdk/issues/19640
var EnvironmentContextKey = struct{}{}

View File

@ -32,6 +32,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### API Breaking Changes
* [#20502](https://github.com/cosmos/cosmos-sdk/pull/20502) `Accept` on the `Authorization` interface now expects the authz environment in the `context.Context`. This is already done when `Accept` is called by `k.DispatchActions`, but should be done manually if `Accept` is called directly.
* [#19783](https://github.com/cosmos/cosmos-sdk/pull/19783) Removes the use of Accounts String() method
* `NewMsgExec`, `NewMsgGrant` and `NewMsgRevoke` now takes strings as arguments instead of `sdk.AccAddress`.
* `ExportGenesis` also returns an error.

View File

@ -9,6 +9,7 @@ import (
"github.com/cosmos/gogoproto/proto"
"cosmossdk.io/core/appmodule"
corecontext "cosmossdk.io/core/context"
errorsmod "cosmossdk.io/errors"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/authz"
@ -83,8 +84,7 @@ func (k Keeper) update(ctx context.Context, grantee, granter sdk.AccAddress, upd
// grants from the message signer to the grantee.
func (k Keeper) DispatchActions(ctx context.Context, grantee sdk.AccAddress, msgs []sdk.Msg) ([][]byte, error) {
results := make([][]byte, len(msgs))
sdkCtx := sdk.UnwrapSDKContext(ctx)
now := sdkCtx.HeaderInfo().Time
now := k.Environment.HeaderService.HeaderInfo(ctx).Time
for i, msg := range msgs {
signers, _, err := k.cdc.GetMsgSigners(msg)
@ -118,7 +118,10 @@ func (k Keeper) DispatchActions(ctx context.Context, grantee sdk.AccAddress, msg
return nil, err
}
resp, err := authorization.Accept(sdkCtx, msg)
// pass the environment in the context
// users on server/v2 are expected to unwrap the environment from the context
// users on baseapp can still unwrap the sdk context
resp, err := authorization.Accept(context.WithValue(ctx, corecontext.EnvironmentContextKey, k.Environment), msg)
if err != nil {
return nil, err
}

View File

@ -1,6 +1,4 @@
package authz
const (
// ModuleName is the module name constant used in many places
ModuleName = "authz"
)
// ModuleName is the module name constant used in many places
const ModuleName = "authz"

View File

@ -1,10 +1,15 @@
package simulation
import (
"context"
"math/rand"
"time"
"cosmossdk.io/core/address"
"cosmossdk.io/core/appmodule"
corecontext "cosmossdk.io/core/context"
coregas "cosmossdk.io/core/gas"
coreheader "cosmossdk.io/core/header"
"cosmossdk.io/x/authz"
"cosmossdk.io/x/authz/keeper"
banktype "cosmossdk.io/x/bank/types"
@ -315,7 +320,12 @@ func SimulateMsgExec(
msg := []sdk.Msg{banktype.NewMsgSend(graStr, greStr, coins)}
_, err = sendAuth.Accept(ctx, msg[0])
goCtx := context.WithValue(ctx.Context(), corecontext.EnvironmentContextKey, appmodule.Environment{
HeaderService: headerService{},
GasService: mockGasService{},
})
_, err = sendAuth.Accept(goCtx, msg[0])
if err != nil {
if sdkerrors.ErrInsufficientFunds.Is(err) {
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, nil
@ -359,3 +369,25 @@ func SimulateMsgExec(
return simtypes.NewOperationMsg(&msgExec, true, "success"), nil, nil
}
}
type headerService struct{}
func (h headerService) HeaderInfo(ctx context.Context) coreheader.Info {
return sdk.UnwrapSDKContext(ctx).HeaderInfo()
}
type mockGasService struct {
coregas.Service
}
func (m mockGasService) GasMeter(ctx context.Context) coregas.Meter {
return mockGasMeter{}
}
type mockGasMeter struct {
coregas.Meter
}
func (m mockGasMeter) Consume(amount coregas.Gas, descriptor string) error {
return nil
}

View File

@ -4,6 +4,8 @@ import (
"context"
"cosmossdk.io/core/address"
"cosmossdk.io/core/appmodule/v2"
corecontext "cosmossdk.io/core/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/authz"
@ -40,12 +42,19 @@ func (a SendAuthorization) Accept(ctx context.Context, msg sdk.Msg) (authz.Accep
return authz.AcceptResponse{}, sdkerrors.ErrInsufficientFunds.Wrapf("requested amount is more than spend limit")
}
authzEnv, ok := ctx.Value(corecontext.EnvironmentContextKey).(appmodule.Environment)
if !ok {
return authz.AcceptResponse{}, sdkerrors.ErrUnauthorized.Wrap("environment not set")
}
isAddrExists := false
toAddr := mSend.ToAddress
allowedList := a.GetAllowList()
sdkCtx := sdk.UnwrapSDKContext(ctx)
for _, addr := range allowedList {
sdkCtx.GasMeter().ConsumeGas(gasCostPerIteration, "send authorization")
if err := authzEnv.GasService.GasMeter(ctx).Consume(gasCostPerIteration, "send authorization"); err != nil {
return authz.AcceptResponse{}, err
}
if addr == toAddr {
isAddrExists = true
break

View File

@ -1,12 +1,16 @@
package types_test
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/require"
"cosmossdk.io/core/header"
"cosmossdk.io/core/appmodule/v2"
corecontext "cosmossdk.io/core/context"
coregas "cosmossdk.io/core/gas"
coreheader "cosmossdk.io/core/header"
sdkmath "cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/bank/types"
@ -25,9 +29,36 @@ var (
unknownAddrStr = "cosmos1ta047h6lw4hxkmn0wah97h6lta0sml880l"
)
type headerService struct{}
func (h headerService) HeaderInfo(ctx context.Context) coreheader.Info {
return sdk.UnwrapSDKContext(ctx).HeaderInfo()
}
type mockGasService struct {
coregas.Service
}
func (m mockGasService) GasMeter(ctx context.Context) coregas.Meter {
return mockGasMeter{}
}
type mockGasMeter struct {
coregas.Meter
}
func (m mockGasMeter) Consume(amount coregas.Gas, descriptor string) error {
return nil
}
func TestSendAuthorization(t *testing.T) {
ac := codectestutil.CodecOptions{}.GetAddressCodec()
ctx := testutil.DefaultContextWithDB(t, storetypes.NewKVStoreKey(types.StoreKey), storetypes.NewTransientStoreKey("transient_test")).Ctx.WithHeaderInfo(header.Info{})
sdkCtx := testutil.DefaultContextWithDB(t, storetypes.NewKVStoreKey(types.StoreKey), storetypes.NewTransientStoreKey("transient_test")).Ctx.WithHeaderInfo(coreheader.Info{})
ctx := context.WithValue(sdkCtx.Context(), corecontext.EnvironmentContextKey, appmodule.Environment{
HeaderService: headerService{},
GasService: mockGasService{},
})
allowList := make([]sdk.AccAddress, 1)
allowList[0] = toAddr
authorization := types.NewSendAuthorization(coins1000, nil, ac)

View File

@ -5,6 +5,8 @@ import (
"fmt"
"cosmossdk.io/core/address"
"cosmossdk.io/core/appmodule"
corecontext "cosmossdk.io/core/context"
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -100,11 +102,17 @@ func (a StakeAuthorization) Accept(ctx context.Context, msg sdk.Msg) (authz.Acce
return authz.AcceptResponse{}, sdkerrors.ErrInvalidRequest.Wrap("unknown msg type")
}
authzEnv, ok := ctx.Value(corecontext.EnvironmentContextKey).(appmodule.Environment)
if !ok {
return authz.AcceptResponse{}, sdkerrors.ErrUnauthorized.Wrap("environment not set")
}
isValidatorExists := false
allowedList := a.GetAllowList().GetAddress()
sdkCtx := sdk.UnwrapSDKContext(ctx)
for _, validator := range allowedList {
sdkCtx.GasMeter().ConsumeGas(gasCostPerIteration, "stake authorization")
if err := authzEnv.GasService.GasMeter(ctx).Consume(gasCostPerIteration, "stake authorization"); err != nil {
return authz.AcceptResponse{}, err
}
if validator == validatorAddress {
isValidatorExists = true
break
@ -113,7 +121,10 @@ func (a StakeAuthorization) Accept(ctx context.Context, msg sdk.Msg) (authz.Acce
denyList := a.GetDenyList().GetAddress()
for _, validator := range denyList {
sdkCtx.GasMeter().ConsumeGas(gasCostPerIteration, "stake authorization")
if err := authzEnv.GasService.GasMeter(ctx).Consume(gasCostPerIteration, "stake authorization"); err != nil {
return authz.AcceptResponse{}, err
}
if validator == validatorAddress {
return authz.AcceptResponse{}, sdkerrors.ErrUnauthorized.Wrapf("cannot delegate/undelegate to %s validator", validator)
}

View File

@ -1,11 +1,15 @@
package types_test
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"cosmossdk.io/core/appmodule/v2"
corecontext "cosmossdk.io/core/context"
coregas "cosmossdk.io/core/gas"
coreheader "cosmossdk.io/core/header"
storetypes "cosmossdk.io/store/types"
stakingtypes "cosmossdk.io/x/staking/types"
@ -39,10 +43,37 @@ func accAddressToString(t *testing.T, addr sdk.AccAddress) string {
return r
}
type headerService struct{}
func (h headerService) HeaderInfo(ctx context.Context) coreheader.Info {
return sdk.UnwrapSDKContext(ctx).HeaderInfo()
}
type mockGasService struct {
coregas.Service
}
func (m mockGasService) GasMeter(ctx context.Context) coregas.Meter {
return mockGasMeter{}
}
type mockGasMeter struct {
coregas.Meter
}
func (m mockGasMeter) Consume(amount coregas.Gas, descriptor string) error {
return nil
}
func TestAuthzAuthorizations(t *testing.T) {
key := storetypes.NewKVStoreKey(stakingtypes.StoreKey)
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
ctx := testCtx.Ctx.WithHeaderInfo(coreheader.Info{})
sdkCtx := testCtx.Ctx.WithHeaderInfo(coreheader.Info{})
ctx := context.WithValue(sdkCtx.Context(), corecontext.EnvironmentContextKey, appmodule.Environment{
HeaderService: headerService{},
GasService: mockGasService{},
})
valAddressCodec := codectestutil.CodecOptions{}.GetValidatorCodec()
// verify ValidateBasic returns error for the AUTHORIZATION_TYPE_UNSPECIFIED authorization type
delAuth, err := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNSPECIFIED, &coin100, valAddressCodec)
@ -313,7 +344,7 @@ func TestAuthzAuthorizations(t *testing.T) {
[]sdk.ValAddress{},
stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
&coin100,
stakingtypes.NewMsgCancelUnbondingDelegation(accAddressToString(t, delAddr), valAddressToString(t, val1), ctx.HeaderInfo().Height, coin100),
stakingtypes.NewMsgCancelUnbondingDelegation(accAddressToString(t, delAddr), valAddressToString(t, val1), sdkCtx.HeaderInfo().Height, coin100),
false,
true,
nil,
@ -324,7 +355,7 @@ func TestAuthzAuthorizations(t *testing.T) {
[]sdk.ValAddress{},
stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
&coin100,
stakingtypes.NewMsgCancelUnbondingDelegation(accAddressToString(t, delAddr), valAddressToString(t, val1), ctx.HeaderInfo().Height, coin50),
stakingtypes.NewMsgCancelUnbondingDelegation(accAddressToString(t, delAddr), valAddressToString(t, val1), sdkCtx.HeaderInfo().Height, coin50),
false,
false,
&stakingtypes.StakeAuthorization{
@ -341,7 +372,7 @@ func TestAuthzAuthorizations(t *testing.T) {
[]sdk.ValAddress{},
stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
&coin100,
stakingtypes.NewMsgCancelUnbondingDelegation(accAddressToString(t, delAddr), valAddressToString(t, val3), ctx.HeaderInfo().Height, coin50),
stakingtypes.NewMsgCancelUnbondingDelegation(accAddressToString(t, delAddr), valAddressToString(t, val3), sdkCtx.HeaderInfo().Height, coin50),
true,
false,
nil,
@ -352,7 +383,7 @@ func TestAuthzAuthorizations(t *testing.T) {
[]sdk.ValAddress{},
stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
nil,
stakingtypes.NewMsgCancelUnbondingDelegation(accAddressToString(t, delAddr), valAddressToString(t, val2), ctx.HeaderInfo().Height, coin100),
stakingtypes.NewMsgCancelUnbondingDelegation(accAddressToString(t, delAddr), valAddressToString(t, val2), sdkCtx.HeaderInfo().Height, coin100),
false,
false,
&stakingtypes.StakeAuthorization{
@ -369,7 +400,7 @@ func TestAuthzAuthorizations(t *testing.T) {
[]sdk.ValAddress{val1},
stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
&coin100,
stakingtypes.NewMsgCancelUnbondingDelegation(accAddressToString(t, delAddr), valAddressToString(t, val1), ctx.HeaderInfo().Height, coin100),
stakingtypes.NewMsgCancelUnbondingDelegation(accAddressToString(t, delAddr), valAddressToString(t, val1), sdkCtx.HeaderInfo().Height, coin100),
true,
false,
nil,