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

Reestimate deterministic gas #646

Merged
merged 30 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
55462bf
Reestimate deterministic gas
Sep 11, 2023
341b9cb
Merge master into wojtek/reestimate-gas
wojtek-coreum Sep 12, 2023
c7cfb1b
fixes
Sep 13, 2023
a4c3db1
fix
Sep 13, 2023
72b3e1f
Merge master into wojtek/reestimate-gas
wojtek-coreum Sep 13, 2023
37a691e
generate
Sep 13, 2023
26205d8
Document procedure
Sep 13, 2023
fd6bafb
remove fixmes
Sep 13, 2023
47b143f
fix
Sep 13, 2023
36fa33e
Merge master into wojtek/reestimate-gas
wojtek-coreum Sep 13, 2023
8ff73e9
Merge master into wojtek/reestimate-gas
wojtek-coreum Sep 14, 2023
03188d2
Merge master into wojtek/reestimate-gas
wojtek-coreum Sep 14, 2023
2824132
Merge master into wojtek/reestimate-gas
wojtek-coreum Sep 14, 2023
92936de
fixes
Sep 14, 2023
a19224c
Merge master into wojtek/reestimate-gas
wojtek-coreum Sep 14, 2023
33d1c5e
fixes
Sep 14, 2023
af18b44
fixes
Sep 14, 2023
4a576b8
Merge master into wojtek/reestimate-gas
wojtek-coreum Sep 15, 2023
28c25f4
fix insufficient funds in authz test
Sep 15, 2023
b69ba8b
executing two messages in single transaction causes bad rounding resu…
Sep 15, 2023
d04a42b
Merge master into wojtek/reestimate-gas
wojtek-coreum Sep 18, 2023
7ce2ee6
fix
Sep 18, 2023
6f299e9
fix
Sep 18, 2023
c001809
Merge remote-tracking branch 'origin/master' into wojtek/reestimate-gas
Oct 5, 2023
9aeb0f2
fixes
Oct 5, 2023
8922300
Move gast estimation test to separate file and use build tag
Oct 6, 2023
c28e9fb
Merge master into wojtek/reestimate-gas
wojtek-coreum Oct 10, 2023
0cec10a
regenerate
Oct 11, 2023
0153b20
fix
Oct 11, 2023
1f2f851
Merge master into wojtek/reestimate-gas
wojtek-coreum Oct 11, 2023
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
6 changes: 3 additions & 3 deletions cmd/cored/cosmoscmd/gas_price_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ func TestAutoGasPrices(t *testing.T) {
},
},
{
name: "specific gas prices is provided",
flags: []string{fmt.Sprintf("--gas-prices=0.1%s", denom), "--gas=100000"},
name: "specific gas prices are provided",
flags: []string{fmt.Sprintf("--gas-prices=0.1%s", denom), "--gas=115000"},
feeAssertion: func(t *testing.T, fee sdk.Coins) {
assert.True(t, fee.IsEqual(sdk.NewCoins(sdk.NewCoin(denom, sdkmath.NewInt(10000)))))
assert.True(t, fee.IsEqual(sdk.NewCoins(sdk.NewCoin(denom, sdkmath.NewInt(11500)))))
},
},
{
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/modules/assetft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2352,7 +2352,7 @@ func TestAssetFTWhitelistIssuerAccount(t *testing.T) {
requireT.ErrorIs(err, cosmoserrors.ErrUnauthorized)
}

// TestBareToken checks non of the features will work if the flags are not set.
// TestBareToken checks none of the features will work if the flags are not set.
func TestBareToken(t *testing.T) {
t.Parallel()

Expand Down
65 changes: 56 additions & 9 deletions integration-tests/modules/authz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,53 @@ import (
"github.com/CoreumFoundation/coreum/v3/testutil/integration"
)

// TestAuthzDirectTransferFails if grantee sends message directly, without using authz.
func TestAuthzDirectTransferFails(t *testing.T) {
t.Parallel()

ctx, chain := integrationtests.NewCoreumTestingContext(t)

requireT := require.New(t)

granter := chain.GenAccount()
grantee := chain.GenAccount()
recipient := chain.GenAccount()

amountToSend := sdkmath.NewInt(1_000)

// init the messages provisionally to use in the authztypes.MsgExec
msgBankSend := &banktypes.MsgSend{
FromAddress: granter.String(),
ToAddress: recipient.String(),
// send a half to have 2 messages in the Exec
Amount: sdk.NewCoins(chain.NewCoin(amountToSend)),
}

chain.FundAccountWithOptions(ctx, t, granter, integration.BalancesOptions{
Messages: []sdk.Msg{
// Grantee signs the transaction, but granter is the sender, so fees are taken from the granter's account.
// In ante handler, fees are deducted before verifying signature, so funding granter to cover the fee is important,
// to verify that transaction is rejected due to invalid signature.
msgBankSend,
},
Amount: amountToSend,
})

// this is done because account must exist to send transaction
chain.FundAccountWithOptions(ctx, t, grantee, integration.BalancesOptions{
Amount: sdk.NewIntFromUint64(1),
})

// try to send from grantee directly
_, err := client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(grantee),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(msgBankSend)),
msgBankSend,
)
requireT.ErrorIs(err, cosmoserrors.ErrInvalidPubKey)
}

// TestAuthz tests the authz module Grant/Execute/Revoke messages execution and their deterministic gas.
func TestAuthz(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -51,6 +98,15 @@ func TestAuthz(t *testing.T) {
Amount: sdk.NewCoins(chain.NewCoin(sdkmath.NewInt(1_000))),
}
execMsg := authztypes.NewMsgExec(grantee, []sdk.Msg{msgBankSend, msgBankSend})

chain.FundAccountWithOptions(ctx, t, granter, integration.BalancesOptions{
Messages: []sdk.Msg{
&authztypes.MsgGrant{},
&authztypes.MsgRevoke{},
},
Amount: totalAmountToSend,
})

chain.FundAccountWithOptions(ctx, t, grantee, integration.BalancesOptions{
Messages: []sdk.Msg{
msgBankSend,
Expand Down Expand Up @@ -85,15 +141,6 @@ func TestAuthz(t *testing.T) {
requireT.NoError(err)
requireT.Equal(1, len(gransRes.Grants))

// try to send from grantee directly
_, err = client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(grantee),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(msgBankSend)),
msgBankSend,
)
requireT.ErrorIs(err, cosmoserrors.ErrInvalidPubKey)

// try to send using the authz
txResult, err = client.BroadcastTx(
ctx,
Expand Down
57 changes: 34 additions & 23 deletions integration-tests/modules/bank_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -493,23 +493,21 @@ func TestBankMultiSend(t *testing.T) {

amount := sdkmath.NewInt(1000)

issueMsgs := []sdk.Msg{
&assetfttypes.MsgIssue{
Issuer: sender.String(),
Symbol: "TOK1",
Subunit: "tok1",
Precision: 1,
Description: "TOK1 Description",
InitialAmount: amount,
},
&assetfttypes.MsgIssue{
Issuer: sender.String(),
Symbol: "TOK2",
Subunit: "tok2",
Precision: 1,
Description: "TOK2 Description",
InitialAmount: amount,
},
issueMsg1 := &assetfttypes.MsgIssue{
Issuer: sender.String(),
Symbol: "TOK1",
Subunit: "tok1",
Precision: 1,
Description: "TOK1 Description",
InitialAmount: amount,
}
issueMsg2 := &assetfttypes.MsgIssue{
Issuer: sender.String(),
Symbol: "TOK2",
Subunit: "tok2",
Precision: 1,
Description: "TOK2 Description",
InitialAmount: amount,
}

chain.FundAccountWithOptions(ctx, t, sender, integration.BalancesOptions{
Expand All @@ -521,25 +519,36 @@ func TestBankMultiSend(t *testing.T) {
{Coins: make(sdk.Coins, 2)},
{Coins: make(sdk.Coins, 2)},
},
}}, issueMsgs...),
Amount: chain.QueryAssetFTParams(ctx, t).IssueFee.Amount.MulRaw(int64(len(issueMsgs))),
}}, issueMsg1, issueMsg2),
Amount: chain.QueryAssetFTParams(ctx, t).IssueFee.Amount.MulRaw(2),
})

// Issue fungible tokens
res, err := client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(sender),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(issueMsgs...)),
issueMsgs...,
chain.TxFactory().WithGas(chain.GasLimitByMsgs(issueMsg1)),
issueMsg1,
)
require.NoError(t, err)

tokenIssuedEvts, err := event.FindTypedEvents[*assetfttypes.EventIssued](res.Events)
require.NoError(t, err)
require.Equal(t, len(issueMsgs), len(tokenIssuedEvts))

denom1 := tokenIssuedEvts[0].Denom
denom2 := tokenIssuedEvts[1].Denom

res, err = client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(sender),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(issueMsg2)),
issueMsg2,
)
require.NoError(t, err)

tokenIssuedEvts, err = event.FindTypedEvents[*assetfttypes.EventIssued](res.Events)
require.NoError(t, err)

denom2 := tokenIssuedEvts[0].Denom

msg := &banktypes.MsgMultiSend{
Inputs: []banktypes.Input{
Expand Down Expand Up @@ -581,6 +590,8 @@ func TestBankMultiSend(t *testing.T) {
require.NoError(t, err)
require.Equal(t, bankMultiSendGas, uint64(res.GasUsed))

// =============================

bankClient := banktypes.NewQueryClient(chain.ClientContext)

qres, err := bankClient.AllBalances(ctx, &banktypes.QueryAllBalancesRequest{Address: sender.String()})
Expand Down
116 changes: 116 additions & 0 deletions integration-tests/modules/gas_estimation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//go:build integrationtests && gasestimationtests

package modules

import (
"fmt"
"testing"

sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"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"
)

// TestBankSendEstimation is used to estimate gas required by each additional token present in bank send message.
// It executes transactions sending from 1 to 201 tokens in single message and reports gas estimated by each of them.
// Then you may copy the results to a spreadsheet and calculate the gas required by each transfer.
// Spreadsheet example might be found here: https://docs.google.com/spreadsheets/d/1qoKa8udTPYdS_-ofJ8xNbnZFDh-gqGb4n0_OgcTHUOA/edit?usp=sharing
// Keep in mind that to estimate the gas you need to move bank send message to nondeterministic section inside deterministic gas config.
func TestBankSendEstimation(t *testing.T) {
const (
nTokens = 101
step = 20
)

requireT := require.New(t)

ctx, chain := integrationtests.NewCoreumTestingContext(t)

issuer := chain.GenAccount()
recipient1 := chain.GenAccount()
recipient2 := chain.GenAccount()

tokens := make([]string, 0, nTokens)
deterministicMsgs := make([]sdk.Msg, 0, 3*nTokens)
sendMsg := &banktypes.MsgSend{
FromAddress: issuer.String(),
ToAddress: recipient1.String(),
}
initialAmount := sdkmath.NewInt(100_000_000_000)
for i := 0; i < nTokens; i++ {
subunit := fmt.Sprintf("tok%d", i)
denom := assetfttypes.BuildDenom(subunit, issuer)
deterministicMsgs = append(deterministicMsgs, &assetfttypes.MsgIssue{
Issuer: issuer.String(),
Symbol: fmt.Sprintf("TOK%d", i),
Subunit: fmt.Sprintf("tok%d", i),
Precision: 1,
Description: fmt.Sprintf("TOK%d", i),
InitialAmount: initialAmount,
Features: []assetfttypes.Feature{
assetfttypes.Feature_minting,
assetfttypes.Feature_burning,
assetfttypes.Feature_freezing,
assetfttypes.Feature_whitelisting,
assetfttypes.Feature_ibc,
},
BurnRate: sdkmath.LegacyMustNewDecFromStr("0.01"),
SendCommissionRate: sdkmath.LegacyMustNewDecFromStr("0.01"),
}, &assetfttypes.MsgSetWhitelistedLimit{
Sender: issuer.String(),
Account: recipient1.String(),
Coin: sdk.NewInt64Coin(denom, 1_000_000_000_000),
}, &assetfttypes.MsgSetWhitelistedLimit{
Sender: issuer.String(),
Account: recipient2.String(),
Coin: sdk.NewInt64Coin(denom, 1_000_000_000_000),
})

tokens = append(tokens, assetfttypes.BuildDenom(subunit, issuer))
sendMsg.Amount = sendMsg.Amount.Add(sdk.NewCoin(denom, initialAmount))
}

chain.FundAccountWithOptions(ctx, t, issuer, integration.BalancesOptions{
Messages: deterministicMsgs,
Amount: chain.QueryAssetFTParams(ctx, t).IssueFee.Amount.MulRaw(nTokens).AddRaw(1_000_000_000),
})

chain.FundAccountWithOptions(ctx, t, recipient1, integration.BalancesOptions{
Amount: sdk.NewIntFromUint64(1_000_000_000),
})

_, err := client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(issuer),
chain.TxFactory().WithGas(chain.GasLimitByMsgs(deterministicMsgs...)+5_000_000),
append(append([]sdk.Msg{}, deterministicMsgs...), sendMsg)...,
)
requireT.NoError(err)

for n := 1; n <= nTokens; n += step {
sendMsg := &banktypes.MsgSend{
FromAddress: recipient1.String(),
ToAddress: recipient2.String(),
}

for i := 0; i < n; i++ {
sendMsg.Amount = sendMsg.Amount.Add(sdk.NewCoin(tokens[i], sdkmath.NewInt(1_0000_000)))
}

txRes, err := client.BroadcastTx(
ctx,
chain.ClientContext.WithFromAddress(recipient1),
chain.TxFactory().WithGas(50_000_000),
sendMsg,
)
requireT.NoError(err)

fmt.Printf("%d\t%d\n", n, txRes.GasUsed)
}
}
4 changes: 2 additions & 2 deletions testutil/integration/coreum.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ func (c CoreumChain) ComputeNeededBalanceFromOptions(options BalancesOptions) sd
gas := c.GasLimitByMsgs(msg)
// Ceil().RoundInt() is here to be compatible with the sdk's TxFactory
// https://github.com/cosmos/cosmos-sdk/blob/ff416ee63d32da5d520a8b2d16b00da762416146/client/tx/factory.go#L223
amt := options.GasPrice.Mul(sdk.NewDec(int64(gas))).Ceil().RoundInt()
amt := options.GasPrice.Mul(sdkmath.LegacyNewDec(int64(gas))).Ceil().RoundInt()
totalAmount = totalAmount.Add(amt)
}

return totalAmount.Add(options.GasPrice.Mul(sdk.NewDec(int64(options.NondeterministicMessagesGas))).Ceil().RoundInt()).Add(options.Amount)
return totalAmount.Add(options.GasPrice.Mul(sdkmath.LegacyNewDec(int64(options.NondeterministicMessagesGas))).Ceil().RoundInt()).Add(options.Amount)
}

// FundAccountWithOptions computes the needed balances and fund account with it.
Expand Down
14 changes: 14 additions & 0 deletions x/deterministicgas/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Gas estimation procedure

After every upgrade of Cosmos SDK (or whenever we decide) we should verify that our deterministic gas estimations for messages are correct.

To do it:
1. Modify crust to deploy `explorer` and `monitoring` profiles when integration tests are started
2. Run integration tests
3. Go to our Grafana dashboard in `znet`
4. Take values for deterministic gas factors reported there
5. Recalculate the deterministic gas by multiplying it by the minimum value taken from the metric.

If there is huge divergence between min and max values reported, reason should be identified.

For Bank Send message we have integration test `TestBankSendEstimation` used to estimate the gas required by each additional coin present in the message.
Loading