-
Notifications
You must be signed in to change notification settings - Fork 409
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement tx counter for transaction info (#621)
* Implement tx counter for transaction info * Count tx before pre-conditions * Doc update
- Loading branch information
Showing
15 changed files
with
267 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package app | ||
|
||
import ( | ||
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/x/auth/ante" | ||
"github.com/cosmos/cosmos-sdk/x/auth/signing" | ||
"github.com/cosmos/cosmos-sdk/x/auth/types" | ||
) | ||
|
||
// NewAnteHandler returns an AnteHandler that checks and increments sequence | ||
// numbers, checks signatures & account numbers, and deducts fees from the first | ||
// signer. | ||
func NewAnteHandler( | ||
ak ante.AccountKeeper, bankKeeper types.BankKeeper, | ||
sigGasConsumer ante.SignatureVerificationGasConsumer, | ||
signModeHandler signing.SignModeHandler, | ||
txCounterStoreKey sdk.StoreKey, | ||
) sdk.AnteHandler { | ||
// copied sdk https://github.com/cosmos/cosmos-sdk/blob/v0.42.9/x/auth/ante/ante.go | ||
return sdk.ChainAnteDecorators( | ||
ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first | ||
wasmkeeper.NewCountTXDecorator(txCounterStoreKey), | ||
ante.NewRejectExtensionOptionsDecorator(), | ||
ante.NewMempoolFeeDecorator(), | ||
ante.NewValidateBasicDecorator(), | ||
ante.TxTimeoutHeightDecorator{}, | ||
ante.NewValidateMemoDecorator(ak), | ||
ante.NewConsumeGasForTxSizeDecorator(ak), | ||
ante.NewRejectFeeGranterDecorator(), | ||
ante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators | ||
ante.NewValidateSigCountDecorator(ak), | ||
ante.NewDeductFeeDecorator(ak, bankKeeper), | ||
ante.NewSigGasConsumeDecorator(ak, sigGasConsumer), | ||
ante.NewSigVerificationDecorator(ak, signModeHandler), | ||
ante.NewIncrementSequenceDecorator(ak), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package keeper | ||
|
||
import ( | ||
"encoding/binary" | ||
"github.com/CosmWasm/wasmd/x/wasm/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
// CountTXDecorator ante handler to count the tx position in a block. | ||
type CountTXDecorator struct { | ||
storeKey sdk.StoreKey | ||
} | ||
|
||
// NewCountTXDecorator constructor | ||
func NewCountTXDecorator(storeKey sdk.StoreKey) *CountTXDecorator { | ||
return &CountTXDecorator{storeKey: storeKey} | ||
} | ||
|
||
// AnteHandle handler stores a tx counter with current height encoded in the store to let the app handle | ||
// global rollback behavior instead of keeping state in the handler itself. | ||
// The ante handler passes the counter value via sdk.Context upstream. See `types.TXCounter(ctx)` to read the value. | ||
// Simulations don't get a tx counter value assigned. | ||
func (a CountTXDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { | ||
if simulate { | ||
return next(ctx, tx, simulate) | ||
} | ||
store := ctx.KVStore(a.storeKey) | ||
currentHeight := ctx.BlockHeight() | ||
|
||
var txCounter uint32 = 0 // start with 0 | ||
// load counter when exists | ||
if bz := store.Get(types.TXCounterPrefix); bz != nil { | ||
lastHeight, val := decodeHeightCounter(bz) | ||
if currentHeight == lastHeight { | ||
// then use stored counter | ||
txCounter = val | ||
} // else use `0` from above to start with | ||
} | ||
// store next counter value for current height | ||
store.Set(types.TXCounterPrefix, encodeHeightCounter(currentHeight, txCounter+1)) | ||
|
||
return next(types.WithTXCounter(ctx, txCounter), tx, simulate) | ||
} | ||
|
||
func encodeHeightCounter(height int64, counter uint32) []byte { | ||
b := make([]byte, 4) | ||
binary.BigEndian.PutUint32(b, counter) | ||
return append(sdk.Uint64ToBigEndian(uint64(height)), b...) | ||
} | ||
|
||
func decodeHeightCounter(bz []byte) (int64, uint32) { | ||
return int64(sdk.BigEndianToUint64(bz[0:8])), binary.BigEndian.Uint32(bz[8:]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package keeper | ||
|
||
import ( | ||
"github.com/CosmWasm/wasmd/x/wasm/types" | ||
"github.com/cosmos/cosmos-sdk/store" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"github.com/tendermint/tendermint/libs/log" | ||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" | ||
dbm "github.com/tendermint/tm-db" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestCountTxDecorator(t *testing.T) { | ||
keyWasm := sdk.NewKVStoreKey(types.StoreKey) | ||
db := dbm.NewMemDB() | ||
ms := store.NewCommitMultiStore(db) | ||
ms.MountStoreWithDB(keyWasm, sdk.StoreTypeIAVL, db) | ||
require.NoError(t, ms.LoadLatestVersion()) | ||
const myCurrentBlockHeight = 100 | ||
|
||
specs := map[string]struct { | ||
setupDB func(t *testing.T, ctx sdk.Context) | ||
simulate bool | ||
nextAssertAnte func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) | ||
expErr bool | ||
}{ | ||
"no initial counter set": { | ||
setupDB: func(t *testing.T, ctx sdk.Context) {}, | ||
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { | ||
gotCounter, ok := types.TXCounter(ctx) | ||
require.True(t, ok) | ||
assert.Equal(t, uint32(0), gotCounter) | ||
// and stored +1 | ||
bz := ctx.MultiStore().GetKVStore(keyWasm).Get(types.TXCounterPrefix) | ||
assert.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 0, 0, 0, 1}, bz) | ||
return ctx, nil | ||
}, | ||
}, | ||
"persistent counter incremented - big endian": { | ||
setupDB: func(t *testing.T, ctx sdk.Context) { | ||
bz := []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 1, 0, 0, 2} | ||
ctx.MultiStore().GetKVStore(keyWasm).Set(types.TXCounterPrefix, bz) | ||
}, | ||
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { | ||
gotCounter, ok := types.TXCounter(ctx) | ||
require.True(t, ok) | ||
assert.Equal(t, uint32(1<<24+2), gotCounter) | ||
// and stored +1 | ||
bz := ctx.MultiStore().GetKVStore(keyWasm).Get(types.TXCounterPrefix) | ||
assert.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 1, 0, 0, 3}, bz) | ||
return ctx, nil | ||
}, | ||
}, | ||
"old height counter replaced": { | ||
setupDB: func(t *testing.T, ctx sdk.Context) { | ||
previousHeight := byte(myCurrentBlockHeight - 1) | ||
bz := []byte{0, 0, 0, 0, 0, 0, 0, previousHeight, 0, 0, 0, 1} | ||
ctx.MultiStore().GetKVStore(keyWasm).Set(types.TXCounterPrefix, bz) | ||
}, | ||
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { | ||
gotCounter, ok := types.TXCounter(ctx) | ||
require.True(t, ok) | ||
assert.Equal(t, uint32(0), gotCounter) | ||
// and stored +1 | ||
bz := ctx.MultiStore().GetKVStore(keyWasm).Get(types.TXCounterPrefix) | ||
assert.Equal(t, []byte{0, 0, 0, 0, 0, 0, 0, myCurrentBlockHeight, 0, 0, 0, 1}, bz) | ||
return ctx, nil | ||
}, | ||
}, | ||
"simulation not persisted": { | ||
setupDB: func(t *testing.T, ctx sdk.Context) { | ||
}, | ||
simulate: true, | ||
nextAssertAnte: func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { | ||
_, ok := types.TXCounter(ctx) | ||
assert.False(t, ok) | ||
require.True(t, simulate) | ||
// and not stored | ||
assert.False(t, ctx.MultiStore().GetKVStore(keyWasm).Has(types.TXCounterPrefix)) | ||
return ctx, nil | ||
}, | ||
}, | ||
} | ||
for name, spec := range specs { | ||
t.Run(name, func(t *testing.T) { | ||
ctx := sdk.NewContext(ms.CacheMultiStore(), tmproto.Header{ | ||
Height: myCurrentBlockHeight, | ||
Time: time.Date(2021, time.September, 27, 12, 0, 0, 0, time.UTC), | ||
}, false, log.NewNopLogger()) | ||
|
||
spec.setupDB(t, ctx) | ||
var anyTx sdk.Tx | ||
|
||
// when | ||
ante := NewCountTXDecorator(keyWasm) | ||
_, gotErr := ante.AnteHandle(ctx, anyTx, spec.simulate, spec.nextAssertAnte) | ||
if spec.expErr { | ||
require.Error(t, gotErr) | ||
return | ||
} | ||
require.NoError(t, gotErr) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package types | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
type contextKey int | ||
|
||
const ( | ||
// private type creates an interface key for Context that cannot be accessed by any other package | ||
contextKeyTXCount contextKey = iota | ||
) | ||
|
||
// WithTXCounter stores a transaction counter value in the context | ||
func WithTXCounter(ctx sdk.Context, counter uint32) sdk.Context { | ||
return ctx.WithValue(contextKeyTXCount, counter) | ||
} | ||
|
||
// TXCounter returns the tx counter value and found bool from the context. | ||
// The result will be (0, false) for external queries or simulations where no counter available. | ||
func TXCounter(ctx sdk.Context) (uint32, bool) { | ||
val, ok := ctx.Value(contextKeyTXCount).(uint32) | ||
return val, ok | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.