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 10 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
98 changes: 98 additions & 0 deletions integration-tests/modules/bank_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,104 @@ func TestBankCoreSend(t *testing.T) {
assert.Equal(t, recipientInitialAmount.Add(amountToSend).String(), balancesRecipient.Balance.Amount.String())
}

// 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 = 201
step = 10
)

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, integrationtests.BalancesOptions{
Messages: deterministicMsgs,
Amount: chain.QueryAssetFTParams(ctx, t).IssueFee.Amount.MulRaw(nTokens).AddRaw(1_000_000_000),
})

chain.FundAccountWithOptions(ctx, t, recipient1, integrationtests.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)
}
}

func assertBatchAccounts(
ctx context.Context,
chain integrationtests.CoreumChain,
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.
94 changes: 50 additions & 44 deletions x/deterministicgas/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ import (

// These constants define gas for messages which have custom calculation logic.
const (
BankSendPerCoinGas = 24000
BankMultiSendPerOperationsGas = 11000
BankSendPerCoinGas = 50000
BankMultiSendPerOperationsGas = 35000
AuthzExecOverhead = 2000
)

Expand Down Expand Up @@ -66,101 +66,107 @@ func DefaultConfig() Config {
FreeSignatures: 1,
}

// FIXME(v47-deterministic): re-estimate and update all values + regenerate doc
cfg.gasByMsg = map[MsgURL]gasByMsgFunc{
// asset/ft
MsgToMsgURL(&assetfttypes.MsgIssue{}): constantGasFunc(70000),
MsgToMsgURL(&assetfttypes.MsgMint{}): constantGasFunc(11000),
MsgToMsgURL(&assetfttypes.MsgBurn{}): constantGasFunc(23000),
MsgToMsgURL(&assetfttypes.MsgFreeze{}): constantGasFunc(5000),
MsgToMsgURL(&assetfttypes.MsgUnfreeze{}): constantGasFunc(2500),
MsgToMsgURL(&assetfttypes.MsgGloballyFreeze{}): constantGasFunc(5000),
MsgToMsgURL(&assetfttypes.MsgMint{}): constantGasFunc(11000 * 2.78),
MsgToMsgURL(&assetfttypes.MsgBurn{}): constantGasFunc(23000 * 1.52),
MsgToMsgURL(&assetfttypes.MsgFreeze{}): constantGasFunc(5000 * 1.72),
MsgToMsgURL(&assetfttypes.MsgUnfreeze{}): constantGasFunc(2500 * 1.54),
MsgToMsgURL(&assetfttypes.MsgGloballyFreeze{}): constantGasFunc(5000 * 1.04),
MsgToMsgURL(&assetfttypes.MsgGloballyUnfreeze{}): constantGasFunc(2500),
MsgToMsgURL(&assetfttypes.MsgSetWhitelistedLimit{}): constantGasFunc(5000),
MsgToMsgURL(&assetfttypes.MsgUpgradeTokenV1{}): constantGasFunc(25000),
MsgToMsgURL(&assetfttypes.MsgSetWhitelistedLimit{}): constantGasFunc(5000 * 1.76),
// TODO: Reestimate when next token upgrade is prepared
MsgToMsgURL(&assetfttypes.MsgUpgradeTokenV1{}): constantGasFunc(25000),

// asset/nft
MsgToMsgURL(&assetnfttypes.MsgBurn{}): constantGasFunc(16000),
MsgToMsgURL(&assetnfttypes.MsgBurn{}): constantGasFunc(16000 * 1.64),
MsgToMsgURL(&assetnfttypes.MsgIssueClass{}): constantGasFunc(16000),
MsgToMsgURL(&assetnfttypes.MsgMint{}): constantGasFunc(39000),
MsgToMsgURL(&assetnfttypes.MsgFreeze{}): constantGasFunc(7000),
MsgToMsgURL(&assetnfttypes.MsgFreeze{}): constantGasFunc(7000 * 1.19),
MsgToMsgURL(&assetnfttypes.MsgUnfreeze{}): constantGasFunc(5000),
MsgToMsgURL(&assetnfttypes.MsgAddToWhitelist{}): constantGasFunc(7000),
MsgToMsgURL(&assetnfttypes.MsgRemoveFromWhitelist{}): constantGasFunc(3500),

// authz
// FIXME (v47-deterministic): We need a procedure to estimate the overhead of the authz. Proposal:
// 1. Estimate normal message
// 2. Estimate the same message executed using authz
// 3. Subtract one from the other
// We should have an integration test doing this.
MsgToMsgURL(&authz.MsgExec{}): cfg.authzMsgExecGasFunc(AuthzExecOverhead),
MsgToMsgURL(&authz.MsgGrant{}): constantGasFunc(7000),
MsgToMsgURL(&authz.MsgRevoke{}): constantGasFunc(2500),
MsgToMsgURL(&authz.MsgGrant{}): constantGasFunc(7000 * 3.95),
MsgToMsgURL(&authz.MsgRevoke{}): constantGasFunc(2500 * 3.19),

// bank
MsgToMsgURL(&banktypes.MsgSend{}): bankSendMsgGasFunc(BankSendPerCoinGas),
MsgToMsgURL(&banktypes.MsgMultiSend{}): bankMultiSendMsgGasFunc(BankMultiSendPerOperationsGas),

// distribution
MsgToMsgURL(&distributiontypes.MsgFundCommunityPool{}): constantGasFunc(15000),
MsgToMsgURL(&distributiontypes.MsgFundCommunityPool{}): constantGasFunc(15000 * 1.11),
MsgToMsgURL(&distributiontypes.MsgSetWithdrawAddress{}): constantGasFunc(5000),
MsgToMsgURL(&distributiontypes.MsgWithdrawDelegatorReward{}): constantGasFunc(65000),
MsgToMsgURL(&distributiontypes.MsgWithdrawDelegatorReward{}): constantGasFunc(65000 * 1.21),
MsgToMsgURL(&distributiontypes.MsgWithdrawValidatorCommission{}): constantGasFunc(22000),

// feegrant
MsgToMsgURL(&feegranttypes.MsgGrantAllowance{}): constantGasFunc(10000),
MsgToMsgURL(&feegranttypes.MsgGrantAllowance{}): constantGasFunc(10000 * 1.10),
MsgToMsgURL(&feegranttypes.MsgRevokeAllowance{}): constantGasFunc(2500),

// gov
// FIXME(v47-deterministic): check that if we want to support both go types
MsgToMsgURL(&govtypesv1beta1.MsgVote{}): constantGasFunc(7000),
MsgToMsgURL(&govtypesv1beta1.MsgVote{}): constantGasFunc(7000 * 0.88),
MsgToMsgURL(&govtypesv1beta1.MsgVoteWeighted{}): constantGasFunc(9000),
MsgToMsgURL(&govtypesv1beta1.MsgDeposit{}): constantGasFunc(52000),
MsgToMsgURL(&govtypesv1beta1.MsgDeposit{}): constantGasFunc(52000 * 1.63),

MsgToMsgURL(&govtypesv1.MsgVote{}): constantGasFunc(7000),
MsgToMsgURL(&govtypesv1.MsgVoteWeighted{}): constantGasFunc(9000),
MsgToMsgURL(&govtypesv1.MsgDeposit{}): constantGasFunc(52000),
MsgToMsgURL(&govtypesv1.MsgVote{}): constantGasFunc(7000 * 0.88),
MsgToMsgURL(&govtypesv1.MsgVoteWeighted{}): constantGasFunc(9000 * 0.72),
// FIXME (v47-deterministic): We must add integration test executing this message to have data to analyze
MsgToMsgURL(&govtypesv1.MsgDeposit{}): constantGasFunc(52000),

// nft
MsgToMsgURL(&nfttypes.MsgSend{}): constantGasFunc(16000),
MsgToMsgURL(&nfttypes.MsgSend{}): constantGasFunc(16000 * 1.55),

// slashing
// FIXME (v47-deterministic): We must add integration test executing this message to have data to analyze
MsgToMsgURL(&slashingtypes.MsgUnjail{}): constantGasFunc(25000),

// staking
MsgToMsgURL(&stakingtypes.MsgDelegate{}): constantGasFunc(69000),
MsgToMsgURL(&stakingtypes.MsgDelegate{}): constantGasFunc(69000 * 1.21),
MsgToMsgURL(&stakingtypes.MsgUndelegate{}): constantGasFunc(112000),
MsgToMsgURL(&stakingtypes.MsgBeginRedelegate{}): constantGasFunc(142000),
MsgToMsgURL(&stakingtypes.MsgCreateValidator{}): constantGasFunc(76000),
MsgToMsgURL(&stakingtypes.MsgBeginRedelegate{}): constantGasFunc(142000 * 1.11),
MsgToMsgURL(&stakingtypes.MsgCreateValidator{}): constantGasFunc(76000 * 1.54),
MsgToMsgURL(&stakingtypes.MsgEditValidator{}): constantGasFunc(13000),

// vesting
MsgToMsgURL(&vestingtypes.MsgCreateVestingAccount{}): constantGasFunc(25000),
MsgToMsgURL(&vestingtypes.MsgCreateVestingAccount{}): constantGasFunc(25000 * 1.2),

// wasm
MsgToMsgURL(&wasmtypes.MsgUpdateAdmin{}): constantGasFunc(8000),
MsgToMsgURL(&wasmtypes.MsgClearAdmin{}): constantGasFunc(6500),

// ibc transfer
MsgToMsgURL(&ibctransfertypes.MsgTransfer{}): constantGasFunc(37000),
MsgToMsgURL(&ibctransfertypes.MsgTransfer{}): constantGasFunc(37000 * 1.47),
}

// FIXME(v47-deterministic): validate all new un-deterministic messages and move to deterministic if possible
registerNondeterministicGasFuncs(
&cfg,
[]sdk.Msg{
// auth
&authtypes.MsgUpdateParams{},
&authtypes.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway

// bank
&banktypes.MsgSetSendEnabled{},
&banktypes.MsgUpdateParams{},
&banktypes.MsgSetSendEnabled{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway
&banktypes.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway

// consensus
&consensustypes.MsgUpdateParams{},
&consensustypes.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway

// crisis
&crisistypes.MsgUpdateParams{},
&crisistypes.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway

// distribution
&distributiontypes.MsgUpdateParams{},
&distributiontypes.MsgCommunityPoolSpend{},
&distributiontypes.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway
&distributiontypes.MsgCommunityPoolSpend{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway

// gov
// MsgSubmitProposal is defined as nondeterministic because it runs a proposal handler function
Expand All @@ -170,7 +176,7 @@ func DefaultConfig() Config {
// FIXME(v47-deterministic): check that if we want to support both go types
&govtypesv1.MsgSubmitProposal{},
&govtypesv1.MsgExecLegacyContent{},
&govtypesv1.MsgUpdateParams{},
&govtypesv1.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway

// crisis
// MsgVerifyInvariant is defined as nondeterministic since fee
Expand All @@ -184,22 +190,22 @@ func DefaultConfig() Config {
&evidencetypes.MsgSubmitEvidence{},

// mint
&minttypes.MsgUpdateParams{},
&minttypes.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway

// staking
&stakingtypes.MsgUpdateParams{},
&stakingtypes.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway
// FIXME(v47-deterministic): add message to deterministic (we have separate task for it)
&stakingtypes.MsgCancelUnbondingDelegation{},

// slashing
&slashingtypes.MsgUpdateParams{},
&slashingtypes.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway

// slashing
&slashingtypes.MsgUpdateParams{},
&slashingtypes.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway

// upgrade
&upgradetypes.MsgCancelUpgrade{},
&upgradetypes.MsgSoftwareUpgrade{},
&upgradetypes.MsgCancelUpgrade{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway
&upgradetypes.MsgSoftwareUpgrade{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway

// vesting
// FIXME(v47-deterministic): add message to deterministic (we have separate task for it)
Expand All @@ -216,7 +222,7 @@ func DefaultConfig() Config {
&wasmtypes.MsgIBCSend{},
&wasmtypes.MsgIBCCloseChannel{},
&wasmtypes.MsgUpdateInstantiateConfig{},
&wasmtypes.MsgUpdateParams{},
&wasmtypes.MsgUpdateParams{}, // This is non-deterministic because all the gov proposals are non-deterministic anyway
&wasmtypes.MsgUnpinCodes{},
&wasmtypes.MsgPinCodes{},
&wasmtypes.MsgSudoContract{},
Expand Down
Loading