feat: simulate nested messages (#20291)
This commit is contained in:
parent
cc5c4d0699
commit
fe8474e9b4
@ -42,6 +42,7 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i
|
||||
|
||||
### Features
|
||||
|
||||
* (baseapp) [#20291](https://github.com/cosmos/cosmos-sdk/pull/20291) Simulate nested messages.
|
||||
* (tests) [#20013](https://github.com/cosmos/cosmos-sdk/pull/20013) Introduce system tests to run multi node local testnet in CI
|
||||
* (runtime) [#19953](https://github.com/cosmos/cosmos-sdk/pull/19953) Implement `core/transaction.Service` in runtime.
|
||||
* (client) [#19905](https://github.com/cosmos/cosmos-sdk/pull/19905) Add grpc client config to `client.toml`.
|
||||
|
||||
33
UPGRADING.md
33
UPGRADING.md
@ -5,6 +5,39 @@ Note, always read the **SimApp** section for more information on application wir
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### BaseApp
|
||||
|
||||
#### Nested Messages Simulation
|
||||
|
||||
Now it is possible to simulate the nested messages of a message, providing developers with a powerful tool for
|
||||
testing and predicting the behavior of complex transactions. This feature allows for a more comprehensive
|
||||
evaluation of gas consumption, state changes, and potential errors that may occur when executing nested
|
||||
messages. However, it's important to note that while the simulation can provide valuable insights, it does not
|
||||
guarantee the correct execution of the nested messages in the future. Factors such as changes in the
|
||||
blockchain state or updates to the protocol could potentially affect the actual execution of these nested
|
||||
messages when the transaction is finally processed on the network.
|
||||
|
||||
For example, consider a governance proposal that includes nested messages to update multiple protocol
|
||||
parameters. At the time of simulation, the blockchain state may be suitable for executing all these nested
|
||||
messages successfully. However, by the time the actual governance proposal is executed (which could be days or
|
||||
weeks later), the blockchain state might have changed significantly. As a result, while the simulation showed
|
||||
a successful execution, the actual governance proposal might fail when it's finally processed.
|
||||
|
||||
By default, when simulating transactions, the gas cost of nested messages is not calculated. This means that
|
||||
only the gas cost of the top-level message is considered. However, this behavior can be customized using the
|
||||
`SetIncludeNestedMsgsGas` option when building the BaseApp. By providing a list of message types to this option,
|
||||
you can specify which messages should have their nested message gas costs included in the simulation. This
|
||||
allows for more accurate gas estimation for transactions involving specific message types that contain nested
|
||||
messages, while maintaining the default behavior for other message types.
|
||||
|
||||
Here is an example on how `SetIncludeNestedMsgsGas` option could be set to calculate the gas of a gov proposal
|
||||
nested messages:
|
||||
```go
|
||||
baseAppOptions = append(baseAppOptions, baseapp.SetIncludeNestedMsgsGas([]sdk.Message{&gov.MsgSubmitProposal{}}))
|
||||
// ...
|
||||
app.App = appBuilder.Build(db, traceStore, baseAppOptions...)
|
||||
```
|
||||
|
||||
### SimApp
|
||||
|
||||
In this section we describe the changes made in Cosmos SDK' SimApp.
|
||||
|
||||
@ -24,6 +24,7 @@ import (
|
||||
"github.com/cosmos/gogoproto/jsonpb"
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
gogotypes "github.com/cosmos/gogoproto/types"
|
||||
any "github.com/cosmos/gogoproto/types/any"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@ -38,6 +39,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/baseapp/testutil/mock"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
"github.com/cosmos/cosmos-sdk/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
@ -760,6 +762,240 @@ func TestABCI_FinalizeBlock_MultiMsg(t *testing.T) {
|
||||
require.Equal(t, int64(2), msgCounter2)
|
||||
}
|
||||
|
||||
func anyMessage(t *testing.T, cdc codec.Codec, msg *baseapptestutil.MsgSend) *any.Any {
|
||||
t.Helper()
|
||||
b, err := cdc.Marshal(msg)
|
||||
require.NoError(t, err)
|
||||
return &any.Any{
|
||||
TypeUrl: sdk.MsgTypeURL(msg),
|
||||
Value: b,
|
||||
}
|
||||
}
|
||||
|
||||
func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
|
||||
anteOpt := func(bapp *baseapp.BaseApp) {
|
||||
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
|
||||
newCtx = ctx.WithGasMeter(storetypes.NewGasMeter(uint64(15)))
|
||||
return
|
||||
})
|
||||
}
|
||||
suite := NewBaseAppSuite(t, anteOpt)
|
||||
|
||||
_, err := suite.baseApp.InitChain(&abci.InitChainRequest{
|
||||
ConsensusParams: &cmtproto.ConsensusParams{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
baseapptestutil.RegisterNestedMessagesServer(suite.baseApp.MsgServiceRouter(), NestedMessgesServerImpl{})
|
||||
baseapptestutil.RegisterSendServer(suite.baseApp.MsgServiceRouter(), SendServerImpl{})
|
||||
|
||||
_, _, addr := testdata.KeyTestPubAddr()
|
||||
_, _, toAddr := testdata.KeyTestPubAddr()
|
||||
tests := []struct {
|
||||
name string
|
||||
message sdk.Msg
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "ok nested message",
|
||||
message: &baseapptestutil.MsgSend{
|
||||
From: addr.String(),
|
||||
To: toAddr.String(),
|
||||
Amount: "10000stake",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "different signers",
|
||||
message: &baseapptestutil.MsgSend{
|
||||
From: toAddr.String(),
|
||||
To: addr.String(),
|
||||
Amount: "10000stake",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty from",
|
||||
message: &baseapptestutil.MsgSend{
|
||||
From: "",
|
||||
To: toAddr.String(),
|
||||
Amount: "10000stake",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty to",
|
||||
message: &baseapptestutil.MsgSend{
|
||||
From: addr.String(),
|
||||
To: "",
|
||||
Amount: "10000stake",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "negative amount",
|
||||
message: &baseapptestutil.MsgSend{
|
||||
From: addr.String(),
|
||||
To: toAddr.String(),
|
||||
Amount: "-10000stake",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "with nested messages",
|
||||
message: &baseapptestutil.MsgNestedMessages{
|
||||
Signer: addr.String(),
|
||||
Messages: []*any.Any{
|
||||
anyMessage(t, suite.cdc, &baseapptestutil.MsgSend{
|
||||
From: addr.String(),
|
||||
To: toAddr.String(),
|
||||
Amount: "10000stake",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with invalid nested messages",
|
||||
message: &baseapptestutil.MsgNestedMessages{
|
||||
Signer: addr.String(),
|
||||
Messages: []*any.Any{
|
||||
anyMessage(t, suite.cdc, &baseapptestutil.MsgSend{
|
||||
From: "",
|
||||
To: toAddr.String(),
|
||||
Amount: "10000stake",
|
||||
}),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "with different signer ",
|
||||
message: &baseapptestutil.MsgNestedMessages{
|
||||
Signer: addr.String(),
|
||||
Messages: []*any.Any{
|
||||
anyMessage(t, suite.cdc, &baseapptestutil.MsgSend{
|
||||
From: toAddr.String(),
|
||||
To: addr.String(),
|
||||
Amount: "10000stake",
|
||||
}),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
nestedMessages := make([]*any.Any, 1)
|
||||
b, err := suite.cdc.Marshal(tt.message)
|
||||
require.NoError(t, err)
|
||||
nestedMessages[0] = &any.Any{
|
||||
TypeUrl: sdk.MsgTypeURL(tt.message),
|
||||
Value: b,
|
||||
}
|
||||
|
||||
msg := &baseapptestutil.MsgNestedMessages{
|
||||
Messages: nestedMessages,
|
||||
Signer: addr.String(),
|
||||
}
|
||||
|
||||
builder := suite.txConfig.NewTxBuilder()
|
||||
err = builder.SetMsgs(msg)
|
||||
require.NoError(t, err)
|
||||
setTxSignature(t, builder, 0)
|
||||
tx := builder.GetTx()
|
||||
|
||||
txBytes, err := suite.txConfig.TxEncoder()(tx)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, result, err := suite.baseApp.Simulate(txBytes)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestABCI_Query_SimulateNestedMessagesGas(t *testing.T) {
|
||||
anteOpt := func(bapp *baseapp.BaseApp) {
|
||||
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
|
||||
newCtx = ctx.WithGasMeter(storetypes.NewGasMeter(uint64(10)))
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
_, _, addr := testdata.KeyTestPubAddr()
|
||||
_, _, toAddr := testdata.KeyTestPubAddr()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
suite *BaseAppSuite
|
||||
message sdk.Msg
|
||||
consumedGas uint64
|
||||
}{
|
||||
{
|
||||
name: "don't add gas",
|
||||
suite: NewBaseAppSuite(t, anteOpt),
|
||||
message: &baseapptestutil.MsgSend{
|
||||
From: addr.String(),
|
||||
To: toAddr.String(),
|
||||
Amount: "10000stake",
|
||||
},
|
||||
consumedGas: 5,
|
||||
},
|
||||
{
|
||||
name: "add gas",
|
||||
suite: NewBaseAppSuite(t, anteOpt, baseapp.SetIncludeNestedMsgsGas([]sdk.Msg{&baseapptestutil.MsgNestedMessages{}})),
|
||||
message: &baseapptestutil.MsgSend{
|
||||
From: addr.String(),
|
||||
To: toAddr.String(),
|
||||
Amount: "10000stake",
|
||||
},
|
||||
consumedGas: 10,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := tt.suite.baseApp.InitChain(&abci.InitChainRequest{
|
||||
ConsensusParams: &cmtproto.ConsensusParams{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
baseapptestutil.RegisterNestedMessagesServer(tt.suite.baseApp.MsgServiceRouter(), NestedMessgesServerImpl{})
|
||||
baseapptestutil.RegisterSendServer(tt.suite.baseApp.MsgServiceRouter(), SendServerImpl{})
|
||||
|
||||
nestedMessages := make([]*any.Any, 1)
|
||||
b, err := tt.suite.cdc.Marshal(tt.message)
|
||||
require.NoError(t, err)
|
||||
nestedMessages[0] = &any.Any{
|
||||
TypeUrl: sdk.MsgTypeURL(tt.message),
|
||||
Value: b,
|
||||
}
|
||||
|
||||
msg := &baseapptestutil.MsgNestedMessages{
|
||||
Messages: nestedMessages,
|
||||
Signer: addr.String(),
|
||||
}
|
||||
|
||||
builder := tt.suite.txConfig.NewTxBuilder()
|
||||
err = builder.SetMsgs(msg)
|
||||
require.NoError(t, err)
|
||||
setTxSignature(t, builder, 0)
|
||||
tx := builder.GetTx()
|
||||
|
||||
txBytes, err := tt.suite.txConfig.TxEncoder()(tx)
|
||||
require.Nil(t, err)
|
||||
|
||||
gas, result, err := tt.suite.baseApp.Simulate(txBytes)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
require.True(t, gas.GasUsed == tt.consumedGas)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestABCI_Query_SimulateTx(t *testing.T) {
|
||||
gasConsumed := uint64(5)
|
||||
anteOpt := func(bapp *baseapp.BaseApp) {
|
||||
|
||||
@ -186,6 +186,9 @@ type BaseApp struct {
|
||||
// including the goroutine handling.This is experimental and must be enabled
|
||||
// by developers.
|
||||
optimisticExec *oe.OptimisticExecution
|
||||
|
||||
// includeNestedMsgsGas holds a set of message types for which gas costs for its nested messages are calculated.
|
||||
includeNestedMsgsGas map[string]struct{}
|
||||
}
|
||||
|
||||
// NewBaseApp returns a reference to an initialized BaseApp. It accepts a
|
||||
@ -233,7 +236,9 @@ func NewBaseApp(
|
||||
if app.interBlockCache != nil {
|
||||
app.cms.SetInterBlockCache(app.interBlockCache)
|
||||
}
|
||||
|
||||
if app.includeNestedMsgsGas == nil {
|
||||
app.includeNestedMsgsGas = make(map[string]struct{})
|
||||
}
|
||||
app.runTxRecoveryMiddleware = newDefaultRecoveryMiddleware()
|
||||
|
||||
// Initialize with an empty interface registry to avoid nil pointer dereference.
|
||||
@ -811,6 +816,10 @@ func (app *BaseApp) endBlock(_ context.Context) (sdk.EndBlock, error) {
|
||||
return endblock, nil
|
||||
}
|
||||
|
||||
type HasNestedMsgs interface {
|
||||
GetMsgs() ([]sdk.Msg, error)
|
||||
}
|
||||
|
||||
// runTx processes a transaction within a given execution mode, encoded transaction
|
||||
// bytes, and the decoded transaction itself. All state transitions occur through
|
||||
// a cached Context depending on the mode provided. State only gets persisted
|
||||
@ -955,6 +964,15 @@ func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, res
|
||||
result, err = app.runMsgs(runMsgCtx, msgs, reflectMsgs, mode)
|
||||
}
|
||||
|
||||
if mode == execModeSimulate {
|
||||
for _, msg := range msgs {
|
||||
nestedErr := app.simulateNestedMessages(ctx, msg)
|
||||
if nestedErr != nil {
|
||||
return gInfo, nil, anteEvents, nestedErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run optional postHandlers (should run regardless of the execution result).
|
||||
//
|
||||
// Note: If the postHandler fails, we also revert the runMsgs state.
|
||||
@ -1061,6 +1079,49 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, reflectMsgs []proto
|
||||
}, nil
|
||||
}
|
||||
|
||||
// simulateNestedMessages simulates a message nested messages.
|
||||
func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg sdk.Msg) error {
|
||||
nestedMsgs, ok := msg.(HasNestedMsgs)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
msgs, err := nestedMsgs.GetMsgs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateBasicTxMsgs(app.msgServiceRouter, msgs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, msg := range msgs {
|
||||
err = app.simulateNestedMessages(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
protoMessages := make([]protoreflect.Message, len(msgs))
|
||||
for i, msg := range msgs {
|
||||
_, protoMsg, err := app.cdc.GetMsgSigners(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
protoMessages[i] = protoMsg
|
||||
}
|
||||
|
||||
initialGas := ctx.GasMeter().GasConsumed()
|
||||
_, err = app.runMsgs(ctx, msgs, protoMessages, execModeSimulate)
|
||||
if err == nil {
|
||||
if _, includeGas := app.includeNestedMsgsGas[sdk.MsgTypeURL(msg)]; !includeGas {
|
||||
consumedGas := ctx.GasMeter().GasConsumed() - initialGas
|
||||
ctx.GasMeter().RefundGas(consumedGas, "simulation of nested messages")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// makeABCIData generates the Data field to be sent to ABCI Check/DeliverTx.
|
||||
func makeABCIData(msgResponses []*codectypes.Any) ([]byte, error) {
|
||||
return proto.Marshal(&sdk.TxMsgData{MsgResponses: msgResponses})
|
||||
|
||||
@ -119,6 +119,19 @@ func SetOptimisticExecution(opts ...func(*oe.OptimisticExecution)) func(*BaseApp
|
||||
}
|
||||
}
|
||||
|
||||
// SetIncludeNestedMsgsGas sets the message types for which gas costs for its nested messages are calculated when simulating.
|
||||
func SetIncludeNestedMsgsGas(msgs []sdk.Msg) func(*BaseApp) {
|
||||
return func(app *BaseApp) {
|
||||
app.includeNestedMsgsGas = make(map[string]struct{})
|
||||
for _, msg := range msgs {
|
||||
if _, ok := msg.(HasNestedMsgs); !ok {
|
||||
continue
|
||||
}
|
||||
app.includeNestedMsgsGas[sdk.MsgTypeURL(msg)] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (app *BaseApp) SetName(name string) {
|
||||
if app.sealed {
|
||||
panic("SetName() on sealed BaseApp")
|
||||
|
||||
@ -3,6 +3,7 @@ package testutil
|
||||
import (
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
|
||||
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -16,10 +17,15 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
|
||||
&MsgCounter{},
|
||||
&MsgCounter2{},
|
||||
&MsgKeyValue{},
|
||||
&MsgNestedMessages{},
|
||||
&MsgSend{},
|
||||
)
|
||||
|
||||
msgservice.RegisterMsgServiceDesc(registry, &_Counter_serviceDesc)
|
||||
msgservice.RegisterMsgServiceDesc(registry, &_Counter2_serviceDesc)
|
||||
msgservice.RegisterMsgServiceDesc(registry, &_KeyValue_serviceDesc)
|
||||
msgservice.RegisterMsgServiceDesc(registry, &_NestedMessages_serviceDesc)
|
||||
msgservice.RegisterMsgServiceDesc(registry, &_Send_serviceDesc)
|
||||
|
||||
codec.RegisterInterfaces(registry)
|
||||
}
|
||||
@ -63,3 +69,21 @@ func (msg *MsgKeyValue) ValidateBasic() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg *MsgNestedMessages) GetMsgs() ([]sdk.Msg, error) {
|
||||
cdc := codectestutil.CodecOptions{}.NewCodec()
|
||||
RegisterInterfaces(cdc.InterfaceRegistry())
|
||||
msgs := make([]sdk.Msg, len(msg.GetMessages()))
|
||||
for i, m := range msg.GetMessages() {
|
||||
mm, err := cdc.InterfaceRegistry().Resolve(m.TypeUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = cdc.UnpackAny(m, &mm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgs[i] = mm
|
||||
}
|
||||
return msgs, nil
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -34,6 +34,25 @@ message MsgKeyValue {
|
||||
|
||||
message MsgCreateKeyValueResponse {}
|
||||
|
||||
message MsgSend {
|
||||
option (cosmos.msg.v1.signer) = "from";
|
||||
|
||||
string from = 1;
|
||||
string to = 2;
|
||||
string amount = 3;
|
||||
}
|
||||
|
||||
message MsgSendResponse {}
|
||||
|
||||
message MsgNestedMessages {
|
||||
option (cosmos.msg.v1.signer) = "signer";
|
||||
|
||||
repeated google.protobuf.Any messages = 1;
|
||||
string signer = 2;
|
||||
}
|
||||
|
||||
message MsgCreateNestedMessagesResponse {}
|
||||
|
||||
service Counter {
|
||||
rpc IncrementCounter(MsgCounter) returns (MsgCreateCounterResponse);
|
||||
}
|
||||
@ -44,4 +63,12 @@ service Counter2 {
|
||||
|
||||
service KeyValue {
|
||||
rpc Set(MsgKeyValue) returns (MsgCreateKeyValueResponse);
|
||||
}
|
||||
}
|
||||
|
||||
service Send {
|
||||
rpc Send(MsgSend) returns (MsgSendResponse);
|
||||
}
|
||||
|
||||
service NestedMessages {
|
||||
rpc Check(MsgNestedMessages) returns (MsgCreateNestedMessagesResponse);
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ import (
|
||||
baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
|
||||
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -375,3 +376,72 @@ func wonkyMsg(t *testing.T, cfg client.TxConfig, tx signing.Tx) signing.Tx {
|
||||
require.NoError(t, err)
|
||||
return builder.GetTx()
|
||||
}
|
||||
|
||||
type SendServerImpl struct {
|
||||
gas uint64
|
||||
}
|
||||
|
||||
func (s SendServerImpl) Send(ctx context.Context, send *baseapptestutil.MsgSend) (*baseapptestutil.MsgSendResponse, error) {
|
||||
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
||||
if send.From == "" {
|
||||
return nil, errors.New("from address cannot be empty")
|
||||
}
|
||||
if send.To == "" {
|
||||
return nil, errors.New("to address cannot be empty")
|
||||
}
|
||||
|
||||
_, err := sdk.ParseCoinNormalized(send.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gas := s.gas
|
||||
if gas == 0 {
|
||||
gas = 5
|
||||
}
|
||||
sdkCtx.GasMeter().ConsumeGas(gas, "send test")
|
||||
return &baseapptestutil.MsgSendResponse{}, nil
|
||||
}
|
||||
|
||||
type NestedMessgesServerImpl struct {
|
||||
gas uint64
|
||||
}
|
||||
|
||||
func (n NestedMessgesServerImpl) Check(ctx context.Context, message *baseapptestutil.MsgNestedMessages) (*baseapptestutil.MsgCreateNestedMessagesResponse, error) {
|
||||
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
||||
cdc := codectestutil.CodecOptions{}.NewCodec()
|
||||
baseapptestutil.RegisterInterfaces(cdc.InterfaceRegistry())
|
||||
|
||||
signer, _, err := cdc.GetMsgSigners(message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(signer) != 1 {
|
||||
return nil, fmt.Errorf("expected 1 signer, got %d", len(signer))
|
||||
}
|
||||
|
||||
msgs, err := message.GetMsgs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, msg := range msgs {
|
||||
s, _, err := cdc.GetMsgSigners(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(s) != 1 {
|
||||
return nil, fmt.Errorf("expected 1 signer, got %d", len(s))
|
||||
}
|
||||
if !bytes.Equal(signer[0], s[0]) {
|
||||
return nil, errors.New("signer does not match")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
gas := n.gas
|
||||
if gas == 0 {
|
||||
gas = 5
|
||||
}
|
||||
sdkCtx.GasMeter().ConsumeGas(gas, "nested messages test")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -66,6 +66,7 @@ import (
|
||||
"cosmossdk.io/x/gov"
|
||||
govkeeper "cosmossdk.io/x/gov/keeper"
|
||||
govtypes "cosmossdk.io/x/gov/types"
|
||||
govv1 "cosmossdk.io/x/gov/types/v1"
|
||||
govv1beta1 "cosmossdk.io/x/gov/types/v1beta1"
|
||||
"cosmossdk.io/x/group"
|
||||
groupkeeper "cosmossdk.io/x/group/keeper"
|
||||
@ -247,7 +248,8 @@ func NewSimApp(
|
||||
voteExtHandler := NewVoteExtensionHandler()
|
||||
voteExtHandler.SetHandlers(bApp)
|
||||
}
|
||||
baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution())
|
||||
baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution(),
|
||||
baseapp.SetIncludeNestedMsgsGas([]sdk.Msg{&govv1.MsgSubmitProposal{}}))
|
||||
|
||||
bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseAppOptions...)
|
||||
bApp.SetCommitMultiStoreTracer(traceStore)
|
||||
|
||||
@ -137,7 +137,6 @@ func NewSimApp(
|
||||
appOpts,
|
||||
// supply the logger
|
||||
logger,
|
||||
|
||||
// ADVANCED CONFIGURATION
|
||||
|
||||
//
|
||||
|
||||
Loading…
Reference in New Issue
Block a user