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

AnteDecorator #5006

Merged
merged 42 commits into from
Oct 10, 2019
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
1960993
start decorator changes
AdityaSripal Aug 28, 2019
12244c5
start replacing ante logic with decorators
AdityaSripal Aug 30, 2019
5a46ceb
Fix build errors
AdityaSripal Sep 5, 2019
bdcf294
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk into ad…
AdityaSripal Sep 5, 2019
ac19f18
fix some tests
AdityaSripal Sep 6, 2019
9a0eac7
fix baseapp tests
AdityaSripal Sep 9, 2019
725f5cf
fix auth tests
AdityaSripal Sep 12, 2019
ff5f1f7
start individual decorator tests
AdityaSripal Sep 14, 2019
73e2739
add signature tests
AdityaSripal Sep 17, 2019
77bc7c6
complete sig tests
AdityaSripal Sep 17, 2019
baf5bfb
fix all test errors
AdityaSripal Sep 17, 2019
71e07ce
remove unnecessary &
AdityaSripal Sep 18, 2019
3b3a74f
fix linter errors
AdityaSripal Sep 18, 2019
6303bec
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk into ad…
AdityaSripal Sep 18, 2019
e52e89d
interface all decorators except sigs
AdityaSripal Sep 18, 2019
ac8209d
check signer lenght in sigverify
AdityaSripal Sep 19, 2019
b93ca36
Apply suggestions from bez code review
AdityaSripal Sep 22, 2019
5825e81
complete bez suggestions
AdityaSripal Sep 29, 2019
441c050
fix merge conflicts
AdityaSripal Sep 29, 2019
2f22251
create sigTx interface
AdityaSripal Oct 1, 2019
975ad0e
linting
AdityaSripal Oct 1, 2019
c5ede73
finish linting except TODO
AdityaSripal Oct 1, 2019
9a5cb61
make auth tx interfaces extend sdk.Tx
AdityaSripal Oct 1, 2019
e7ec478
Merge branch 'master' into aditya/ante-decorator
alexanderbez Oct 2, 2019
634c1aa
test docs, replace FeeCoins with GetFee
AdityaSripal Oct 2, 2019
66144bf
Merge branch 'aditya/ante-decorator' of https://github.com/cosmos/cos…
AdityaSripal Oct 2, 2019
0c39cd9
Apply suggestions from fede code review
AdityaSripal Oct 2, 2019
29e85bf
address tim comments
AdityaSripal Oct 3, 2019
f1964cf
Merge branch 'aditya/ante-decorator' of https://github.com/cosmos/cos…
AdityaSripal Oct 3, 2019
b6d9166
Merge branch 'master' into aditya/ante-decorator
fedekunze Oct 8, 2019
0fd1234
add order comments
AdityaSripal Oct 8, 2019
aad9673
Add Schwarzenegger art
AdityaSripal Oct 8, 2019
5ce99da
add assertions that StdTx implements all necessary decorator interfaces
AdityaSripal Oct 8, 2019
bd79e58
Merge branch 'aditya/ante-decorator' of https://github.com/cosmos/cos…
AdityaSripal Oct 8, 2019
2f54876
documentation and CHANGELOG
AdityaSripal Oct 9, 2019
a4ef3ec
Run goimports
alexanderbez Oct 9, 2019
33cc17a
Update ChainAnteDecorators godoc
alexanderbez Oct 10, 2019
882fac5
Changelog entries cleanup
alexanderbez Oct 10, 2019
163da7f
Changelog entries cleanup
alexanderbez Oct 10, 2019
492cf0d
Merge branch 'master' into aditya/ante-decorator
alexanderbez Oct 10, 2019
1b1a779
Fix formatter
alexanderbez Oct 10, 2019
440a05c
Merge branch 'master' into aditya/ante-decorator
alexanderbez Oct 10, 2019
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
12 changes: 8 additions & 4 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
// performance benefits, but it'll be more difficult to get right.
anteCtx, msCache = app.cacheTxContext(ctx, txBytes)

newCtx, result, abort := app.anteHandler(anteCtx, tx, mode == runTxModeSimulate)
newCtx, err := app.anteHandler(anteCtx, tx, mode == runTxModeSimulate)
if !newCtx.IsZero() {
// At this point, newCtx.MultiStore() is cache-wrapped, or something else
// replaced by the ante handler. We want the original multistore, not one
Expand All @@ -597,10 +597,14 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
ctx = newCtx.WithMultiStore(ms)
}

gasWanted = result.GasWanted
// GasMeter expected to be set in AnteHandler
gasWanted = ctx.GasMeter().Limit()

if abort {
return result
if err != nil {
res := sdk.ResultFromError(err)
res.GasWanted = gasWanted
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this is not specific to this PR, but I've always felt that GasRequired would be a better name than GasWanted

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! Think it should be a separate PR tho

res.GasUsed = ctx.GasMeter().GasConsumed()
return res
}

msCache.Write()
Expand Down
52 changes: 23 additions & 29 deletions baseapp/baseapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/cosmos/cosmos-sdk/store/rootmulti"
store "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var (
Expand Down Expand Up @@ -594,15 +595,18 @@ func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder {
}

func anteHandlerTxTest(t *testing.T, capKey *sdk.KVStoreKey, storeKey []byte) sdk.AnteHandler {
return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
store := ctx.KVStore(capKey)
txTest := tx.(txTest)

if txTest.FailOnAnte {
return newCtx, sdk.ErrInternal("ante handler failure").Result(), true
return newCtx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure")
}

res = incrementingCounter(t, store, storeKey, txTest.Counter)
res := incrementingCounter(t, store, storeKey, txTest.Counter)
if !res.IsOK() {
err = sdkerrors.ABCIError(string(res.Codespace), uint32(res.Code), res.Log)
}
return
}
}
Expand Down Expand Up @@ -835,7 +839,7 @@ func TestSimulateTx(t *testing.T) {
gasConsumed := uint64(5)

anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasConsumed))
return
})
Expand Down Expand Up @@ -896,7 +900,7 @@ func TestSimulateTx(t *testing.T) {

func TestRunInvalidTransaction(t *testing.T) {
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
return
})
}
Expand Down Expand Up @@ -980,17 +984,19 @@ func TestRunInvalidTransaction(t *testing.T) {
func TestTxGasLimits(t *testing.T) {
gasGranted := uint64(10)
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted))

// AnteHandlers must have their own defer/recover in order for the BaseApp
// to know how much gas was used! This is because the GasMeter is created in
// the AnteHandler, but if it panics the context won't be set properly in
// runTx's recover call.
defer func() {
if r := recover(); r != nil {
switch rType := r.(type) {
case sdk.ErrorOutOfGas:
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
res = sdk.ErrOutOfGas(log).Result()
res.GasWanted = gasGranted
res.GasUsed = newCtx.GasMeter().GasConsumed()
err = sdkerrors.Wrap(sdkerrors.ErrOutOfGas, log)
default:
panic(r)
}
Expand All @@ -999,9 +1005,7 @@ func TestTxGasLimits(t *testing.T) {

count := tx.(*txTest).Counter
newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante")
res = sdk.Result{
GasWanted: gasGranted,
}

return
})

Expand Down Expand Up @@ -1065,17 +1069,14 @@ func TestTxGasLimits(t *testing.T) {
func TestMaxBlockGasLimits(t *testing.T) {
gasGranted := uint64(10)
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted))

defer func() {
if r := recover(); r != nil {
switch rType := r.(type) {
case sdk.ErrorOutOfGas:
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
res = sdk.ErrOutOfGas(log).Result()
res.GasWanted = gasGranted
res.GasUsed = newCtx.GasMeter().GasConsumed()
err = sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, "out of gas in location: %v", rType.Descriptor)
default:
panic(r)
}
Expand All @@ -1084,9 +1085,7 @@ func TestMaxBlockGasLimits(t *testing.T) {

count := tx.(*txTest).Counter
newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante")
res = sdk.Result{
GasWanted: gasGranted,
}

return
})

Expand Down Expand Up @@ -1234,17 +1233,15 @@ func TestBaseAppAnteHandler(t *testing.T) {
func TestGasConsumptionBadTx(t *testing.T) {
gasWanted := uint64(5)
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasWanted))

defer func() {
if r := recover(); r != nil {
switch rType := r.(type) {
case sdk.ErrorOutOfGas:
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
res = sdk.ErrOutOfGas(log).Result()
res.GasWanted = gasWanted
res.GasUsed = newCtx.GasMeter().GasConsumed()
err = sdkerrors.Wrap(sdkerrors.ErrOutOfGas, log)
default:
panic(r)
}
Expand All @@ -1254,12 +1251,9 @@ func TestGasConsumptionBadTx(t *testing.T) {
txTest := tx.(txTest)
newCtx.GasMeter().ConsumeGas(uint64(txTest.Counter), "counter-ante")
if txTest.FailOnAnte {
return newCtx, sdk.ErrInternal("ante handler failure").Result(), true
return newCtx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure")
}

res = sdk.Result{
GasWanted: gasWanted,
}
return
})
}
Expand Down Expand Up @@ -1310,7 +1304,7 @@ func TestGasConsumptionBadTx(t *testing.T) {
func TestQuery(t *testing.T) {
key, value := []byte("hello"), []byte("goodbye")
anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
store := ctx.KVStore(capKey1)
store.Set(key, value)
return
Expand Down
14 changes: 7 additions & 7 deletions docs/architecture/adr-010-modular-antehandler.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,16 +191,16 @@ type AnteDecorator interface {
```

```go
// ChainDecorators will recursively link all of the AnteDecorators in the chain and return a final AnteHandler function
// ChainAnteDecorators will recursively link all of the AnteDecorators in the chain and return a final AnteHandler function
// This is done to preserve the ability to set a single AnteHandler function in the baseapp.
func ChainDecorators(chain ...AnteDecorator) AnteHandler {
func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler {
if len(chain) == 1 {
return func(ctx Context, tx Tx, simulate bool) {
chain[0].AnteHandle(ctx, tx, simulate, nil)
}
}
return func(ctx Context, tx Tx, simulate bool) {
chain[0].AnteHandle(ctx, tx, simulate, ChainDecorators(chain[1:]))
chain[0].AnteHandle(ctx, tx, simulate, ChainAnteDecorators(chain[1:]))
}
}
```
Expand All @@ -211,9 +211,9 @@ Define AnteDecorator functions

```go
// Setup GasMeter, catch OutOfGasPanic and handle appropriately
type SetupDecorator struct{}
type SetUpContextDecorator struct{}

func (sud SetupDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) {
func (sud SetUpContextDecorator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error) {
ctx.GasMeter = NewGasMeter(tx.Gas)

defer func() {
Expand Down Expand Up @@ -251,7 +251,7 @@ Link AnteDecorators to create a final AnteHandler. Set this AnteHandler in basea

```go
// Create final antehandler by chaining the decorators together
antehandler := ChainDecorators(NewSetupDecorator(), NewSigVerifyDecorator(), NewUserDefinedDecorator())
antehandler := ChainAnteDecorators(NewSetUpContextDecorator(), NewSigVerifyDecorator(), NewUserDefinedDecorator())

// Set chained Antehandler in the baseapp
bapp.SetAnteHandler(antehandler)
Expand All @@ -264,7 +264,7 @@ Pros:

Cons:

1. Decorator pattern may have a deeply nested structure that is hard to understand, this is mitigated by having the decorator order explicitly listed in the `ChainDecorators` function.
1. Decorator pattern may have a deeply nested structure that is hard to understand, this is mitigated by having the decorator order explicitly listed in the `ChainAnteDecorators` function.
2. Does not make use of the ModuleManager design. Since this is already being used for BeginBlocker/EndBlocker, this proposal seems unaligned with that design pattern.

## Status
Expand Down
34 changes: 33 additions & 1 deletion types/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,36 @@ type Handler func(ctx Context, msg Msg) Result

// AnteHandler authenticates transactions, before their internal messages are handled.
// If newCtx.IsZero(), ctx is used instead.
type AnteHandler func(ctx Context, tx Tx, simulate bool) (newCtx Context, result Result, abort bool)
type AnteHandler func(ctx Context, tx Tx, simulate bool) (newCtx Context, err error)

// AnteDecorator wraps the next AnteHandler to perform custom pre- and post-processing.
type AnteDecorator interface {
AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error)
}

// ChainDecorator chains AnteDecorators together with each element
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
// wrapping over the decorators further along chain and returns a single AnteHandler.
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
//
// First element is outermost decorator, last element is innermost decorator
func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler {
if (chain[len(chain)-1] != Terminator{}) {
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
chain = append(chain, Terminator{})
}
if len(chain) == 1 {
return func(ctx Context, tx Tx, simulate bool) (Context, error) {
return chain[0].AnteHandle(ctx, tx, simulate, nil)
}
}
return func(ctx Context, tx Tx, simulate bool) (Context, error) {
return chain[0].AnteHandle(ctx, tx, simulate, ChainAnteDecorators(chain[1:]...))
}
}

// Terminator AnteDecorator will get added to the chain to simplify decorator code
// Don't need to check if next == nil further up the chain
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
type Terminator struct{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lololol


// Simply return provided Context and nil error
func (t Terminator) AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (Context, error) {
return ctx, nil
}
5 changes: 0 additions & 5 deletions x/auth/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,9 @@ var (
// functions aliases
NewAnteHandler = ante.NewAnteHandler
GetSignerAcc = ante.GetSignerAcc
ValidateSigCount = ante.ValidateSigCount
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
ValidateMemo = ante.ValidateMemo
ProcessPubKey = ante.ProcessPubKey
DefaultSigVerificationGasConsumer = ante.DefaultSigVerificationGasConsumer
DeductFees = ante.DeductFees
EnsureSufficientMempoolFees = ante.EnsureSufficientMempoolFees
SetGasMeter = ante.SetGasMeter
GetSignBytes = ante.GetSignBytes
NewAccountKeeper = keeper.NewAccountKeeper
NewQuerier = keeper.NewQuerier
NewBaseAccount = types.NewBaseAccount
Expand Down
Loading