mukan-sdk/x/auth/ante/sigverify_test.go
Mukan Erkin Törük 20afb5db80
Some checks failed
Build SimApp / build (amd64) (push) Waiting to run
Build SimApp / build (arm64) (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
Build & Push / build (push) Waiting to run
Run Gosec / Gosec (push) Waiting to run
Lint / golangci-lint (push) Waiting to run
Checks dependencies and mocks generation / Check go mod tidy (push) Waiting to run
Checks dependencies and mocks generation / Check up to date mocks (push) Waiting to run
System Tests / setup (push) Waiting to run
System Tests / test-system (push) Blocked by required conditions
System Tests / test-system-legacy (push) Blocked by required conditions
Tests / Code Coverage / split-test-files (push) Waiting to run
Tests / Code Coverage / tests (00) (push) Blocked by required conditions
Tests / Code Coverage / tests (01) (push) Blocked by required conditions
Tests / Code Coverage / tests (02) (push) Blocked by required conditions
Tests / Code Coverage / tests (03) (push) Blocked by required conditions
Tests / Code Coverage / test-integration (push) Waiting to run
Tests / Code Coverage / test-e2e (push) Waiting to run
Tests / Code Coverage / repo-analysis (push) Blocked by required conditions
Tests / Code Coverage / test-sim-nondeterminism (push) Waiting to run
Tests / Code Coverage / test-clientv2 (push) Waiting to run
Tests / Code Coverage / test-core (push) Waiting to run
Tests / Code Coverage / test-depinject (push) Waiting to run
Tests / Code Coverage / test-errors (push) Waiting to run
Tests / Code Coverage / test-math (push) Waiting to run
Tests / Code Coverage / test-schema (push) Waiting to run
Tests / Code Coverage / test-collections (push) Waiting to run
Tests / Code Coverage / test-cosmovisor (push) Waiting to run
Tests / Code Coverage / test-confix (push) Waiting to run
Tests / Code Coverage / test-store (push) Waiting to run
Tests / Code Coverage / test-log (push) Waiting to run
Tests / Code Coverage / test-x-tx (push) Waiting to run
Tests / Code Coverage / test-x-nft (push) Waiting to run
Tests / Code Coverage / test-x-circuit (push) Waiting to run
Tests / Code Coverage / test-x-feegrant (push) Waiting to run
Tests / Code Coverage / test-x-evidence (push) Waiting to run
Tests / Code Coverage / test-x-upgrade (push) Waiting to run
Tests / Code Coverage / test-tools-benchmark (push) Waiting to run
Build & Push SDK Proto Builder / build (push) Has been cancelled
initial: sovereign Mukan Network fork
2026-05-11 03:18:24 +03:00

455 lines
18 KiB
Go

package ante_test
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256r1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
authsign "github.com/cosmos/cosmos-sdk/x/auth/signing"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
"github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)
func TestSetPubKey(t *testing.T) {
suite := SetupTestSuite(t, true)
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()
// keys and addresses
priv1, pub1, addr1 := testdata.KeyTestPubAddr()
priv2, pub2, addr2 := testdata.KeyTestPubAddr()
priv3, pub3, addr3 := testdata.KeyTestPubAddrSecp256R1(t)
addrs := []sdk.AccAddress{addr1, addr2, addr3}
pubs := []cryptotypes.PubKey{pub1, pub2, pub3}
msgs := make([]sdk.Msg, len(addrs))
// set accounts and create msg for each address
for i, addr := range addrs {
acc := suite.accountKeeper.NewAccountWithAddress(suite.ctx, addr)
require.NoError(t, acc.SetAccountNumber(uint64(i+1000)))
suite.accountKeeper.SetAccount(suite.ctx, acc)
msgs[i] = testdata.NewTestMsg(addr)
}
require.NoError(t, suite.txBuilder.SetMsgs(msgs...))
suite.txBuilder.SetFeeAmount(testdata.NewTestFeeAmount())
suite.txBuilder.SetGasLimit(testdata.NewTestGasLimit())
privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0}
tx, err := suite.CreateTestTx(suite.ctx, privs, accNums, accSeqs, suite.ctx.ChainID(), signing.SignMode_SIGN_MODE_DIRECT)
require.NoError(t, err)
spkd := ante.NewSetPubKeyDecorator(suite.accountKeeper)
antehandler := sdk.ChainAnteDecorators(spkd)
ctx, err := antehandler(suite.ctx, tx, false)
require.NoError(t, err)
// Require that all accounts have pubkey set after Decorator runs
for i, addr := range addrs {
pk, err := suite.accountKeeper.GetPubKey(ctx, addr)
require.NoError(t, err, "Error on retrieving pubkey from account")
require.True(t, pubs[i].Equals(pk),
"Wrong Pubkey retrieved from AccountKeeper, idx=%d\nexpected=%s\n got=%s", i, pubs[i], pk)
}
}
// TestSetPubKey_UnorderedNoEvents tests that when the tx is unordered, the sequence event is not emitted.
func TestSetPubKey_UnorderedNoEvents(t *testing.T) {
suite := SetupTestSuite(t, true)
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()
// prepare accounts for tx
priv1, _, addr1 := testdata.KeyTestPubAddr()
acc := suite.accountKeeper.NewAccountWithAddress(suite.ctx, addr1)
require.NoError(t, acc.SetAccountNumber(uint64(1000)))
suite.accountKeeper.SetAccount(suite.ctx, acc)
require.NoError(t, suite.txBuilder.SetMsgs(testdata.NewTestMsg(addr1)))
privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0}
tx, err := suite.CreateTestUnorderedTx(suite.ctx, privs, accNums, accSeqs, suite.ctx.ChainID(), signing.SignMode_SIGN_MODE_DIRECT, true, time.Unix(100, 0))
require.NoError(t, err)
spkd := ante.NewSetPubKeyDecorator(suite.accountKeeper)
antehandler := sdk.ChainAnteDecorators(spkd)
ctx, err := antehandler(suite.ctx.WithBlockTime(time.Unix(95, 0)), tx, false)
require.NoError(t, err)
events := ctx.EventManager().Events()
for _, event := range events {
// if this event were emitted, the tx search by address/sequence would break when an unordered
// transaction uses the same sequence number as another transaction from the same sender.
require.NotContains(t, event.Attributes, sdk.AttributeKeyAccountSequence)
}
}
func TestConsumeSignatureVerificationGas(t *testing.T) {
suite := SetupTestSuite(t, true)
params := types.DefaultParams()
msg := []byte{1, 2, 3, 4}
p := types.DefaultParams()
skR1, _ := secp256r1.GenPrivKey()
pkSet1, sigSet1 := generatePubKeysAndSignatures(5, msg, false)
multisigKey1 := kmultisig.NewLegacyAminoPubKey(2, pkSet1)
multisignature1 := multisig.NewMultisig(len(pkSet1))
expectedCost1 := expectedGasCostByKeys(pkSet1)
for i := range pkSet1 {
stdSig := legacytx.StdSignature{PubKey: pkSet1[i], Signature: sigSet1[i]} //nolint:staticcheck // SA1019: legacytx.StdSignature is deprecated
sigV2, err := legacytx.StdSignatureToSignatureV2(suite.clientCtx.LegacyAmino, stdSig)
require.NoError(t, err)
err = multisig.AddSignatureV2(multisignature1, sigV2, pkSet1)
require.NoError(t, err)
}
type args struct {
meter storetypes.GasMeter
sig signing.SignatureData
pubkey cryptotypes.PubKey
params types.Params
}
tests := []struct {
name string
args args
gasConsumed uint64
shouldErr bool
}{
{"PubKeyEd25519", args{storetypes.NewInfiniteGasMeter(), nil, ed25519.GenPrivKey().PubKey(), params}, p.SigVerifyCostED25519, false},
{"PubKeySecp256k1", args{storetypes.NewInfiniteGasMeter(), nil, secp256k1.GenPrivKey().PubKey(), params}, p.SigVerifyCostSecp256k1, false},
{"PubKeySecp256r1", args{storetypes.NewInfiniteGasMeter(), nil, skR1.PubKey(), params}, p.SigVerifyCostSecp256r1(), false},
{"Multisig", args{storetypes.NewInfiniteGasMeter(), multisignature1, multisigKey1, params}, expectedCost1, false},
{"unknown key", args{storetypes.NewInfiniteGasMeter(), nil, nil, params}, 0, true},
}
for _, tt := range tests {
sigV2 := signing.SignatureV2{
PubKey: tt.args.pubkey,
Data: tt.args.sig,
Sequence: 0, // Arbitrary account sequence
}
err := ante.DefaultSigVerificationGasConsumer(tt.args.meter, sigV2, tt.args.params)
if tt.shouldErr {
require.NotNil(t, err)
} else {
require.Nil(t, err)
require.Equal(t, tt.gasConsumed, tt.args.meter.GasConsumed(), fmt.Sprintf("%d != %d", tt.gasConsumed, tt.args.meter.GasConsumed()))
}
}
}
func TestSigVerification(t *testing.T) {
suite := SetupTestSuite(t, true)
suite.txBankKeeper.EXPECT().DenomMetadata(gomock.Any(), gomock.Any()).Return(&banktypes.QueryDenomMetadataResponse{}, nil).AnyTimes()
enabledSignModes := []signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT, signing.SignMode_SIGN_MODE_TEXTUAL, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON}
// Since TEXTUAL is not enabled by default, we create a custom TxConfig
// here which includes it.
txConfigOpts := authtx.ConfigOptions{
TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(suite.clientCtx),
EnabledSignModes: enabledSignModes,
}
var err error
suite.clientCtx.TxConfig, err = authtx.NewTxConfigWithOptions(
codec.NewProtoCodec(suite.encCfg.InterfaceRegistry),
txConfigOpts,
)
require.NoError(t, err)
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()
// make block height non-zero to ensure account numbers part of signBytes
suite.ctx = suite.ctx.WithBlockHeight(1)
// keys and addresses
priv1, _, addr1 := testdata.KeyTestPubAddr()
priv2, _, addr2 := testdata.KeyTestPubAddr()
priv3, _, addr3 := testdata.KeyTestPubAddr()
addrs := []sdk.AccAddress{addr1, addr2, addr3}
msgs := make([]sdk.Msg, len(addrs))
accs := make([]sdk.AccountI, len(addrs))
// set accounts and create msg for each address
for i, addr := range addrs {
acc := suite.accountKeeper.NewAccountWithAddress(suite.ctx, addr)
require.NoError(t, acc.SetAccountNumber(uint64(i)+1000))
suite.accountKeeper.SetAccount(suite.ctx, acc)
msgs[i] = testdata.NewTestMsg(addr)
accs[i] = acc
}
feeAmount := testdata.NewTestFeeAmount()
gasLimit := testdata.NewTestGasLimit()
spkd := ante.NewSetPubKeyDecorator(suite.accountKeeper)
txConfigOpts = authtx.ConfigOptions{
TextualCoinMetadataQueryFn: txmodule.NewBankKeeperCoinMetadataQueryFn(suite.txBankKeeper),
EnabledSignModes: enabledSignModes,
}
anteTxConfig, err := authtx.NewTxConfigWithOptions(
codec.NewProtoCodec(suite.encCfg.InterfaceRegistry),
txConfigOpts,
)
require.NoError(t, err)
svd := ante.NewSigVerificationDecorator(suite.accountKeeper, anteTxConfig.SignModeHandler())
antehandler := sdk.ChainAnteDecorators(spkd, svd)
defaultSignMode, err := authsign.APISignModeToInternal(anteTxConfig.SignModeHandler().DefaultMode())
require.NoError(t, err)
type testCase struct {
name string
privs []cryptotypes.PrivKey
accNums []uint64
accSeqs []uint64
invalidSigs bool // used for testing sigverify on RecheckTx
recheck bool
sigverify bool
shouldErr bool
}
validSigs := false
testCases := []testCase{
{"no signers", []cryptotypes.PrivKey{}, []uint64{}, []uint64{}, validSigs, false, true, true},
{"not enough signers", []cryptotypes.PrivKey{priv1, priv2}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber()}, []uint64{0, 0}, validSigs, false, true, true},
{"wrong order signers", []cryptotypes.PrivKey{priv3, priv2, priv1}, []uint64{accs[2].GetAccountNumber(), accs[1].GetAccountNumber(), accs[0].GetAccountNumber()}, []uint64{0, 0, 0}, validSigs, false, true, true},
{"wrong accnums", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{7, 8, 9}, []uint64{0, 0, 0}, validSigs, false, true, true},
{"wrong sequences", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber(), accs[2].GetAccountNumber()}, []uint64{3, 4, 5}, validSigs, false, true, true},
{"valid tx", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber(), accs[2].GetAccountNumber()}, []uint64{0, 0, 0}, validSigs, false, true, false},
{"sigverify tx with wrong order signers", []cryptotypes.PrivKey{priv3, priv2, priv1}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber(), accs[2].GetAccountNumber()}, []uint64{0, 0, 0}, validSigs, false, true, true},
{"skip sigverify tx with wrong order signers", []cryptotypes.PrivKey{priv3, priv2, priv1}, []uint64{accs[0].GetAccountNumber(), accs[1].GetAccountNumber(), accs[2].GetAccountNumber()}, []uint64{0, 0, 0}, validSigs, false, false, false},
{"no err on recheck", []cryptotypes.PrivKey{priv1, priv2, priv3}, []uint64{0, 0, 0}, []uint64{0, 0, 0}, !validSigs, true, true, false},
}
for i, tc := range testCases {
for _, signMode := range enabledSignModes {
t.Run(fmt.Sprintf("%s with %s", tc.name, signMode), func(t *testing.T) {
suite.ctx = suite.ctx.WithIsReCheckTx(tc.recheck).WithIsSigverifyTx(tc.sigverify)
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // Create new txBuilder for each test
require.NoError(t, suite.txBuilder.SetMsgs(msgs...))
suite.txBuilder.SetFeeAmount(feeAmount)
suite.txBuilder.SetGasLimit(gasLimit)
tx, err := suite.CreateTestTx(suite.ctx, tc.privs, tc.accNums, tc.accSeqs, suite.ctx.ChainID(), signMode)
require.NoError(t, err)
if tc.invalidSigs {
txSigs, _ := tx.GetSignaturesV2()
badSig, _ := tc.privs[0].Sign([]byte("unrelated message"))
txSigs[0] = signing.SignatureV2{
PubKey: tc.privs[0].PubKey(),
Data: &signing.SingleSignatureData{
SignMode: defaultSignMode,
Signature: badSig,
},
Sequence: tc.accSeqs[0],
}
require.NoError(t, suite.txBuilder.SetSignatures(txSigs...))
tx = suite.txBuilder.GetTx()
}
txBytes, err := suite.clientCtx.TxConfig.TxEncoder()(tx)
require.NoError(t, err)
byteCtx := suite.ctx.WithTxBytes(txBytes)
_, err = antehandler(byteCtx, tx, false)
if tc.shouldErr {
require.NotNil(t, err, "TestCase %d: %s did not error as expected", i, tc.name)
} else {
require.Nil(t, err, "TestCase %d: %s errored unexpectedly. Err: %v", i, tc.name, err)
}
})
}
}
}
func TestSigIntegration(t *testing.T) {
// generate private keys
privs := []cryptotypes.PrivKey{
secp256k1.GenPrivKey(),
secp256k1.GenPrivKey(),
secp256k1.GenPrivKey(),
}
params := types.DefaultParams()
initialSigCost := params.SigVerifyCostSecp256k1
initialCost, err := runSigDecorators(t, params, false, privs...)
require.Nil(t, err)
params.SigVerifyCostSecp256k1 *= 2
doubleCost, err := runSigDecorators(t, params, false, privs...)
require.Nil(t, err)
require.Equal(t, initialSigCost*uint64(len(privs)), doubleCost-initialCost)
}
func runSigDecorators(t *testing.T, params types.Params, _ bool, privs ...cryptotypes.PrivKey) (storetypes.Gas, error) {
t.Helper()
suite := SetupTestSuite(t, true)
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()
// Make block-height non-zero to include accNum in SignBytes
suite.ctx = suite.ctx.WithBlockHeight(1)
err := suite.accountKeeper.Params.Set(suite.ctx, params)
require.NoError(t, err)
msgs := make([]sdk.Msg, len(privs))
accNums := make([]uint64, len(privs))
accSeqs := make([]uint64, len(privs))
// set accounts and create msg for each address
for i, priv := range privs {
addr := sdk.AccAddress(priv.PubKey().Address())
acc := suite.accountKeeper.NewAccountWithAddress(suite.ctx, addr)
require.NoError(t, acc.SetAccountNumber(uint64(i)+1000))
suite.accountKeeper.SetAccount(suite.ctx, acc)
msgs[i] = testdata.NewTestMsg(addr)
accNums[i] = acc.GetAccountNumber()
accSeqs[i] = uint64(0)
}
require.NoError(t, suite.txBuilder.SetMsgs(msgs...))
feeAmount := testdata.NewTestFeeAmount()
gasLimit := testdata.NewTestGasLimit()
suite.txBuilder.SetFeeAmount(feeAmount)
suite.txBuilder.SetGasLimit(gasLimit)
tx, err := suite.CreateTestTx(suite.ctx, privs, accNums, accSeqs, suite.ctx.ChainID(), signing.SignMode_SIGN_MODE_DIRECT)
require.NoError(t, err)
spkd := ante.NewSetPubKeyDecorator(suite.accountKeeper)
svgc := ante.NewSigGasConsumeDecorator(suite.accountKeeper, ante.DefaultSigVerificationGasConsumer)
svd := ante.NewSigVerificationDecorator(suite.accountKeeper, suite.clientCtx.TxConfig.SignModeHandler())
antehandler := sdk.ChainAnteDecorators(spkd, svgc, svd)
txBytes, err := suite.clientCtx.TxConfig.TxEncoder()(tx)
require.NoError(t, err)
suite.ctx = suite.ctx.WithTxBytes(txBytes)
// Determine gas consumption of antehandler with default params
before := suite.ctx.GasMeter().GasConsumed()
ctx, err := antehandler(suite.ctx, tx, false)
after := ctx.GasMeter().GasConsumed()
return after - before, err
}
func TestIncrementSequenceDecorator_ShouldFailWhenUnorderedTxsDisabled(t *testing.T) {
suite := SetupTestSuite(t, true)
isd := ante.NewIncrementSequenceDecorator(suite.accountKeeper)
antehandler := sdk.ChainAnteDecorators(isd)
priv, _, _ := testdata.KeyTestPubAddr()
tx, err := suite.CreateTestUnorderedTx(suite.ctx, []cryptotypes.PrivKey{priv}, []uint64{0}, []uint64{0}, suite.ctx.ChainID(), signing.SignMode_SIGN_MODE_DIRECT, true, time.Now())
require.NoError(t, err)
_, err = antehandler(suite.ctx, tx, false)
require.ErrorContains(t, err, "unordered transactions are disabled")
}
func TestIncrementSequenceDecorator(t *testing.T) {
suite := SetupTestSuiteWithUnordered(t, true, true)
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()
priv, _, addr := testdata.KeyTestPubAddr()
acc := suite.accountKeeper.NewAccountWithAddress(suite.ctx, addr)
require.NoError(t, acc.SetAccountNumber(uint64(50)))
suite.accountKeeper.SetAccount(suite.ctx, acc)
msgs := []sdk.Msg{testdata.NewTestMsg(addr)}
require.NoError(t, suite.txBuilder.SetMsgs(msgs...))
privs := []cryptotypes.PrivKey{priv}
accNums := []uint64{suite.accountKeeper.GetAccount(suite.ctx, addr).GetAccountNumber()}
accSeqs := []uint64{suite.accountKeeper.GetAccount(suite.ctx, addr).GetSequence()}
feeAmount := testdata.NewTestFeeAmount()
gasLimit := testdata.NewTestGasLimit()
suite.txBuilder.SetFeeAmount(feeAmount)
suite.txBuilder.SetGasLimit(gasLimit)
isd := ante.NewIncrementSequenceDecorator(suite.accountKeeper)
antehandler := sdk.ChainAnteDecorators(isd)
testCases := []struct {
name string
ctx sdk.Context
simulate bool
createTx func() sdk.Tx
expectSeqInc bool
}{
{
"inc on recheck no sim",
suite.ctx.WithIsReCheckTx(true),
false,
func() sdk.Tx {
tx, err := suite.CreateTestTx(suite.ctx, privs, accNums, accSeqs, suite.ctx.ChainID(), signing.SignMode_SIGN_MODE_DIRECT)
require.NoError(t, err)
return tx
},
true,
},
{
"inc on no recheck, no sim",
suite.ctx.WithIsReCheckTx(false),
false,
func() sdk.Tx {
tx, err := suite.CreateTestTx(suite.ctx, privs, accNums, accSeqs, suite.ctx.ChainID(), signing.SignMode_SIGN_MODE_DIRECT)
require.NoError(t, err)
return tx
},
true,
},
{
"inc on recheck and sim",
suite.ctx.WithIsReCheckTx(true),
true,
func() sdk.Tx {
tx, err := suite.CreateTestTx(suite.ctx, privs, accNums, accSeqs, suite.ctx.ChainID(), signing.SignMode_SIGN_MODE_DIRECT)
require.NoError(t, err)
return tx
},
true,
},
{
"unordered tx should not inc sequence",
suite.ctx.WithIsReCheckTx(true),
true,
func() sdk.Tx {
tx, err := suite.CreateTestUnorderedTx(suite.ctx, privs, accNums, accSeqs, suite.ctx.ChainID(), signing.SignMode_SIGN_MODE_DIRECT, true, time.Now().Add(time.Hour))
require.NoError(t, err)
return tx
},
false,
},
}
for i, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
beforeSeq := suite.accountKeeper.GetAccount(suite.ctx, addr).GetSequence()
_, err := antehandler(tc.ctx, tc.createTx(), tc.simulate)
require.NoError(t, err, "unexpected error; tc #%d, %v", i, tc)
afterSeq := suite.accountKeeper.GetAccount(suite.ctx, addr).GetSequence()
if tc.expectSeqInc {
require.Equal(t, beforeSeq+1, afterSeq)
} else {
require.Equal(t, beforeSeq, afterSeq)
}
})
}
}