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

Add CalculateGas integration-tests & improve deterministicgas docs #706

Merged
merged 17 commits into from
Nov 13, 2023
152 changes: 146 additions & 6 deletions integration-tests/modules/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import (
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
cosmoserrors "github.com/cosmos/cosmos-sdk/types/errors"
authztypes "github.com/cosmos/cosmos-sdk/x/authz"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/samber/lo"
"github.com/stretchr/testify/require"

integrationtests "github.com/CoreumFoundation/coreum/v3/integration-tests"
"github.com/CoreumFoundation/coreum/v3/pkg/client"
"github.com/CoreumFoundation/coreum/v3/testutil/integration"
assetfttypes "github.com/CoreumFoundation/coreum/v3/x/asset/ft/types"
"github.com/CoreumFoundation/coreum/v3/x/deterministicgas"
)

// TestAuthFeeLimits verifies that invalid message gas won't be accepted.
Expand Down Expand Up @@ -128,11 +131,11 @@ func TestAuthMultisig(t *testing.T) {
recipient := chain.GenAccount()
amountToSendFromMultisigAccount := int64(1000)

multisigPublicKey, keyNamesSet, err := chain.GenMultisigAccount(3, 2)
signersCount := 7
multisigTreshold := 6
multisigPublicKey, keyNamesSet, err := chain.GenMultisigAccount(signersCount, multisigTreshold)
requireT.NoError(err)
multisigAddress := sdk.AccAddress(multisigPublicKey.Address())
signer1KeyName := keyNamesSet[0]
signer2KeyName := keyNamesSet[1]

bankClient := banktypes.NewQueryClient(chain.ClientContext)

Expand All @@ -158,19 +161,31 @@ func TestAuthMultisig(t *testing.T) {
// We do it to test simulation for multisig account.
chain.TxFactory().WithSimulateAndExecute(true),
bankSendMsg,
signer1KeyName)
keyNamesSet[0])
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)
t.Log("Partially signed tx executed with expected error")

_, estimatedGas, err := client.CalculateGas(
ctx,
chain.ClientContext.WithFromAddress(multisigAddress),
chain.TxFactory().WithGasAdjustment(1.0),
bankSendMsg,
)
requireT.NoError(err)

// sign and submit with the min threshold
txRes, err := chain.SignAndBroadcastMultisigTx(
ctx,
chain.ClientContext.WithFromAddress(multisigAddress),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(bankSendMsg)),
bankSendMsg,
signer1KeyName, signer2KeyName)
keyNamesSet[:multisigTreshold]...)
requireT.NoError(err)
t.Logf("Fully signed tx executed, txHash:%s", txRes.TxHash)
t.Logf("Fully signed tx executed, txHash:%s, gas:%d", txRes.TxHash, txRes.GasUsed)

//requireT.Equal(txRes.GasUsed, txRes.GasWanted) // another option to reproduce is to use chain.TxFactory().WithSimulateAndExecute(true) & this assertion.

requireT.Equal(txRes.GasUsed, int64(estimatedGas)) // this shouldn't fail.

recipientBalances, err := bankClient.AllBalances(ctx, &banktypes.QueryAllBalancesRequest{
Address: recipientAddr,
Expand Down Expand Up @@ -212,3 +227,128 @@ func TestAuthUnexpectedSequenceNumber(t *testing.T) {
msg)
require.True(t, cosmoserrors.ErrWrongSequence.Is(err))
}

func TestGasEstimation(t *testing.T) {
t.Parallel()

ctx, chain := integrationtests.NewCoreumTestingContext(t)

sender := chain.GenAccount()

multisigPublicKey1, _, err := chain.GenMultisigAccount(3, 2)
require.NoError(t, err)
multisigAddress1 := sdk.AccAddress(multisigPublicKey1.Address())

multisigPublicKey2, _, err := chain.GenMultisigAccount(7, 6)
require.NoError(t, err)
multisigAddress2 := sdk.AccAddress(multisigPublicKey2.Address())

dgc := deterministicgas.DefaultConfig()

// For accounts to exist on chain we need to fund them at least with min amount (1ucore).
chain.FundAccountWithOptions(ctx, t, sender, integration.BalancesOptions{Amount: sdkmath.NewInt(1)})
chain.FundAccountWithOptions(ctx, t, multisigAddress1, integration.BalancesOptions{Amount: sdkmath.NewInt(1)})
chain.FundAccountWithOptions(ctx, t, multisigAddress2, integration.BalancesOptions{Amount: sdkmath.NewInt(1)})

//initialPayload, err := json.Marshal(moduleswasm.SimpleState{
// Count: 1337,
//})
//requireT.NoError(err)
//contractAddr, codeID, err := chain.Wasm.DeployAndInstantiateWASMContract(
// ctx,
// chain.TxFactory().WithSimulateAndExecute(true),
// admin,
// moduleswasm.SimpleStateWASM,
// integration.InstantiateConfig{
// AccessType: wasmtypes.AccessTypeUnspecified,
// Payload: initialPayload,
// Label: "simple_state",
// },
//)
//requireT.NoError(err)
//chain.Wasm.ExecuteWASMContract()

tests := []struct {
name string
fromAddress sdk.AccAddress
msgs []sdk.Msg
expectedGas uint64
}{
{
name: "singlesig_bank_send",
fromAddress: sender,
msgs: []sdk.Msg{
&banktypes.MsgSend{
FromAddress: sender.String(),
ToAddress: sender.String(),
Amount: sdk.NewCoins(chain.NewCoin(sdkmath.NewInt(1))),
},
},
// single signature no extra bytes.
expectedGas: dgc.FixedGas + 1*deterministicgas.BankSendPerCoinGas,
},
{
name: "multisig_2_3_bank_send",
fromAddress: multisigAddress1,
msgs: []sdk.Msg{
&banktypes.MsgSend{
FromAddress: multisigAddress1.String(),
ToAddress: multisigAddress1.String(),
Amount: sdk.NewCoins(chain.NewCoin(sdkmath.NewInt(1))),
},
},
// single signature no extra bytes.
// Note that multisig account and multiple signatures in a single tx are different.
// Multisig tx still has single signature which is combination of multiple signatures so gas is charged for single sig.
// Tx containing multiple signatures is a different case and gas is charged for each standalone signature.
expectedGas: dgc.FixedGas + 1*deterministicgas.BankSendPerCoinGas,
},
// FIXME: This test fails. Probably because of bug.
//{
// name: "multisig_6_7_bank_send",
// fromAddress: multisigAddress2,
// msgs: []sdk.Msg{
// &banktypes.MsgSend{
// FromAddress: multisigAddress2.String(),
// ToAddress: multisigAddress2.String(),
// Amount: sdk.NewCoins(chain.NewCoin(sdkmath.NewInt(1))),
// },
// },
// expectedGas: dgc.FixedGas + 1*deterministicgas.BankSendPerCoinGas,
// expectedGasAllowedDelta: 0,
//},
{
name: "singlesig_auth_exec_and_bank_send",
fromAddress: sender,
msgs: []sdk.Msg{
lo.ToPtr(
authztypes.NewMsgExec(sender, []sdk.Msg{
&banktypes.MsgSend{
FromAddress: sender.String(),
ToAddress: sender.String(),
Amount: sdk.NewCoins(chain.NewCoin(sdkmath.NewInt(1))),
},
})),
&banktypes.MsgSend{
FromAddress: sender.String(),
ToAddress: sender.String(),
Amount: sdk.NewCoins(chain.NewCoin(sdkmath.NewInt(1))),
},
},
// single signature no extra bytes.
expectedGas: dgc.FixedGas + 1*deterministicgas.BankSendPerCoinGas + (1*deterministicgas.AuthzExecOverhead + 1*deterministicgas.BankSendPerCoinGas),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
_, estimatedGas, err := client.CalculateGas(
ctx,
chain.ClientContext.WithFromAddress(test.fromAddress),
chain.TxFactory(),
test.msgs...,
)
require.NoError(t, err)
require.Equal(t, test.expectedGas, estimatedGas)
})
}
}
3 changes: 1 addition & 2 deletions integration-tests/modules/bank_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,10 @@ func TestBankSendGasEstimation(t *testing.T) {
Amount: sdk.NewCoins(chain.NewCoin(amountToSend)),
}

clientCtx := chain.ClientContext.WithFromAddress(sender)
bankSendGas := chain.GasLimitByMsgs(&banktypes.MsgSend{})
_, estimatedGas, err := client.CalculateGas(
ctx,
clientCtx,
chain.ClientContext.WithFromAddress(sender),
chain.TxFactory().
WithGas(bankSendGas),
msg)
Expand Down
19 changes: 17 additions & 2 deletions pkg/client/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@ func BroadcastTx(ctx context.Context, clientCtx Context, txf Factory, msgs ...sd

// CalculateGas simulates the execution of a transaction and returns the
// simulation response obtained by the query and the adjusted gas amount.
//
// FIXME(v47-multisig-calculate-gas-test) add test to calculate
// The main differences between our version and the one from cosmos-sdk are:
// - we respect context.Context
// - it correctly works when estimating for multisig accounts
func CalculateGas(ctx context.Context, clientCtx Context, txf Factory, msgs ...sdk.Msg) (*sdktx.SimulateResponse, uint64, error) {
txf, err := prepareFactory(ctx, clientCtx, txf)
if err != nil {
Expand Down Expand Up @@ -181,10 +182,24 @@ func CalculateGas(ctx context.Context, clientCtx Context, txf Factory, msgs ...s
signature,
},
})
fmt.Printf("tx bytes len: %v\n", len(txBytes))
if err != nil {
return nil, 0, errors.WithStack(err)
}

txTx, err := clientCtx.TxConfig().TxDecoder()(txBytes)
if err != nil {
fmt.Printf("err: %v\n", err)
}

txJSON, err := clientCtx.TxConfig().TxJSONEncoder()(txTx)
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
_ = txJSON
fmt.Printf("txJSON: %s\n", txJSON)
}

txSvcClient := sdktx.NewServiceClient(clientCtx)
simRes, err := txSvcClient.Simulate(ctx, &sdktx.SimulateRequest{
TxBytes: txBytes,
Expand Down
4 changes: 2 additions & 2 deletions x/deterministicgas/spec/README.tmpl.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Let's say we have a transaction with 1 messages of type
signatures and the tx size is 1000 bytes, total will be:

`
TotalGas = {{ .FixedGas }} + 1 * {{ .BankSendPerCoinGas }} + 1 * {{ .SigVerifyCost }} + max(0, 1000-{{ .FreeBytes }}) * {{ .TxSizeCostPerByte }}
TotalGas = {{ .FixedGas }} + 1 * {{ .BankSendPerCoinGas }} + (1 - 1) * {{ .SigVerifyCost }} + max(0, 1000-{{ .FreeBytes }}) * {{ .TxSizeCostPerByte }}
`

#### Example 2
Expand All @@ -58,7 +58,7 @@ Let's say we have a transaction with 2 messages of type
signatures and the tx size is 2050 bytes, total will be:

`
TotalGas = {{ .FixedGas }} + 2 * {{ .MsgIssueGasPrice }} + 2 * {{ .SigVerifyCost }} + max(0, 2050-{{ .FreeBytes }}) * {{ .TxSizeCostPerByte }}
TotalGas = {{ .FixedGas }} + 2 * {{ .MsgIssueGasPrice }} + (2 - 1) * {{ .SigVerifyCost }} + max(0, 2050-{{ .FreeBytes }}) * {{ .TxSizeCostPerByte }}
`

## Gas Tables
Expand Down
Loading