From 33536b02387a6eba1f090e568d9f3dbd5f6be048 Mon Sep 17 00:00:00 2001 From: Evan Forbes <42654277+evan-forbes@users.noreply.github.com> Date: Thu, 17 Aug 2023 13:33:34 -0500 Subject: [PATCH] feat!: add feegranter option to txsim (#2291) ## Overview This PR adds feegrant back to txsim. Except this time instead of only using feegrant, we now have feegrant as an option. This way we can test without it enabled, which keeps our testing environments _slightly_ more realistic while still being able to enable it when convient. This is particularly useful when we don't want to send gobs of testnet tokens around or have a very long running sequence. closes #2289 blocking #2197 --- app/test/square_size_test.go | 1 + test/cmd/txsim/cli.go | 3 +++ test/e2e/simple_test.go | 2 +- test/txsim/account.go | 42 +++++++++++++++++++++++++++++------- test/txsim/blob.go | 26 ++++++++++++++++------ test/txsim/run.go | 5 +++-- test/txsim/run_test.go | 16 ++++++++++++++ test/txsim/send.go | 7 +++--- test/txsim/sequence.go | 2 +- test/txsim/stake.go | 8 +++++-- 10 files changed, 88 insertions(+), 24 deletions(-) diff --git a/app/test/square_size_test.go b/app/test/square_size_test.go index f2025181d6..03486c29ce 100644 --- a/app/test/square_size_test.go +++ b/app/test/square_size_test.go @@ -139,6 +139,7 @@ func (s *SquareSizeIntegrationTest) fillBlocks(blobSize, blobsPerPFB, pfbsPerBlo "", rand.Int63(), time.Second, + false, seqs..., ) diff --git a/test/cmd/txsim/cli.go b/test/cmd/txsim/cli.go index 550b6beb49..f34d56edaa 100644 --- a/test/cmd/txsim/cli.go +++ b/test/cmd/txsim/cli.go @@ -39,6 +39,7 @@ var ( pollTime time.Duration send, sendIterations, sendAmount int stake, stakeValue, blob int + useFeegrant bool ) func main() { @@ -162,6 +163,7 @@ well funded account that can act as the master account. The command runs until a masterAccName, seed, pollTime, + useFeegrant, sequences..., ) if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { @@ -192,6 +194,7 @@ func flags() *flag.FlagSet { flags.IntVar(&blob, "blob", 0, "number of blob sequences to run") flags.StringVar(&blobSizes, "blob-sizes", "100-1000", "range of blob sizes to send") flags.StringVar(&blobAmounts, "blob-amounts", "1", "range of blobs to send per PFB in a sequence") + flags.BoolVar(&useFeegrant, "feegrant", false, "use the feegrant module to pay for fees") return flags } diff --git a/test/e2e/simple_test.go b/test/e2e/simple_test.go index c3f47c3834..175ff39627 100644 --- a/test/e2e/simple_test.go +++ b/test/e2e/simple_test.go @@ -41,7 +41,7 @@ func TestE2ESimple(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - err = txsim.Run(ctx, testnet.RPCEndpoints(), testnet.GRPCEndpoints(), kr, "", seed, 3*time.Second, sequences...) + err = txsim.Run(ctx, testnet.RPCEndpoints(), testnet.GRPCEndpoints(), kr, "", seed, 3*time.Second, false, sequences...) require.True(t, errors.Is(err, context.DeadlineExceeded), err.Error()) blockchain, err := testnode.ReadBlockchain(context.Background(), testnet.Node(0).AddressRPC()) diff --git a/test/txsim/account.go b/test/txsim/account.go index 47cdd98023..61905ccf19 100644 --- a/test/txsim/account.go +++ b/test/txsim/account.go @@ -17,6 +17,7 @@ import ( authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" auth "github.com/cosmos/cosmos-sdk/x/auth/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/feegrant" "github.com/rs/zerolog/log" ) @@ -32,6 +33,7 @@ type AccountManager struct { mtx sync.Mutex masterAccount *Account accounts map[string]*Account + useFeegrant bool } type Account struct { @@ -42,7 +44,14 @@ type Account struct { Balance int64 } -func NewAccountManager(ctx context.Context, keys keyring.Keyring, masterAccName string, txClient *TxClient, queryClient *QueryClient) (*AccountManager, error) { +func NewAccountManager( + ctx context.Context, + keys keyring.Keyring, + masterAccName string, + txClient *TxClient, + queryClient *QueryClient, + useFeegrant bool, +) (*AccountManager, error) { records, err := keys.List() if err != nil { return nil, err @@ -53,11 +62,12 @@ func NewAccountManager(ctx context.Context, keys keyring.Keyring, masterAccName } am := &AccountManager{ - keys: keys, - accounts: make(map[string]*Account), - pending: make([]*Account, 0), - tx: txClient, - query: queryClient, + keys: keys, + accounts: make(map[string]*Account), + pending: make([]*Account, 0), + tx: txClient, + query: queryClient, + useFeegrant: useFeegrant, } if masterAccName == "" { @@ -233,6 +243,10 @@ func (am *AccountManager) Submit(ctx context.Context, op Operation) error { } } + if am.useFeegrant { + builder.SetFeeGranter(am.masterAccount.Address) + } + if err := am.signTransaction(builder); err != nil { return err } @@ -272,17 +286,29 @@ func (am *AccountManager) GenerateAccounts(ctx context.Context) error { } msgs := make([]types.Msg, 0) + gasLimit := 0 // batch together all the messages needed to create all the accounts for _, acc := range am.pending { if am.masterAccount.Balance < acc.Balance { - return fmt.Errorf("master account has insufficient funds needed: %v", acc.Balance) + return fmt.Errorf("master account has insufficient funds. has: %v needed: %v", am.masterAccount.Balance, acc.Balance) + } + + if am.useFeegrant { + // create a feegrant message so that the master account pays for all the fees of the sub accounts + feegrantMsg, err := feegrant.NewMsgGrantAllowance(&feegrant.BasicAllowance{}, am.masterAccount.Address, acc.Address) + if err != nil { + return fmt.Errorf("error creating feegrant message: %w", err) + } + msgs = append(msgs, feegrantMsg) + gasLimit += FeegrantGasLimit } bankMsg := bank.NewMsgSend(am.masterAccount.Address, acc.Address, types.NewCoins(types.NewInt64Coin(appconsts.BondDenom, acc.Balance))) msgs = append(msgs, bankMsg) + gasLimit += SendGasLimit } - err := am.Submit(ctx, Operation{Msgs: msgs, GasLimit: SendGasLimit * uint64(len(am.pending))}) + err := am.Submit(ctx, Operation{Msgs: msgs, GasLimit: uint64(gasLimit)}) if err != nil { return fmt.Errorf("error funding accounts: %w", err) } diff --git a/test/txsim/blob.go b/test/txsim/blob.go index 33f2a03db2..e42cfe635c 100644 --- a/test/txsim/blob.go +++ b/test/txsim/blob.go @@ -15,7 +15,7 @@ import ( var _ Sequence = &BlobSequence{} // As napkin math, this would cover the cost of 8267 4KB blobs -const fundsForGas = 1e9 // 1000 TIA +const fundsForGas int = 1e9 // 1000 TIA // BlobSequence defines a pattern whereby a single user repeatedly sends a pay for blob // message roughly every height. The PFB may consist of several blobs @@ -24,7 +24,8 @@ type BlobSequence struct { sizes Range blobsPerPFB Range - account types.AccAddress + account types.AccAddress + useFeegrant bool } func NewBlobSequence(sizes, blobsPerPFB Range) *BlobSequence { @@ -53,8 +54,13 @@ func (s *BlobSequence) Clone(n int) []Sequence { return sequenceGroup } -func (s *BlobSequence) Init(_ context.Context, _ grpc.ClientConn, allocateAccounts AccountAllocator, _ *rand.Rand) { - s.account = allocateAccounts(1, fundsForGas)[0] +func (s *BlobSequence) Init(_ context.Context, _ grpc.ClientConn, allocateAccounts AccountAllocator, _ *rand.Rand, useFeegrant bool) { + s.useFeegrant = useFeegrant + funds := fundsForGas + if useFeegrant { + funds = 1 + } + s.account = allocateAccounts(1, funds)[0] } func (s *BlobSequence) Next(_ context.Context, _ grpc.ClientConn, rand *rand.Rand) (Operation, error) { @@ -85,7 +91,7 @@ func (s *BlobSequence) Next(_ context.Context, _ grpc.ClientConn, rand *rand.Ran return Operation{ Msgs: []types.Msg{msg}, Blobs: blobs, - GasLimit: estimateGas(sizes), + GasLimit: estimateGas(sizes, s.useFeegrant), }, nil } @@ -107,11 +113,17 @@ func (r Range) Rand(rand *rand.Rand) int { } // estimateGas estimates the gas required to pay for a set of blobs in a PFB. -func estimateGas(blobSizes []int) uint64 { +func estimateGas(blobSizes []int, useFeegrant bool) uint64 { size := make([]uint32, len(blobSizes)) for i, s := range blobSizes { size[i] = uint32(s) } - return blob.DefaultEstimateGas(size) + // account for the extra gas required to pay for the fee granter + extra := uint64(0) + if useFeegrant { + extra = 12000 + } + + return blob.DefaultEstimateGas(size) + extra } diff --git a/test/txsim/run.go b/test/txsim/run.go index 285538d38f..d7dae27118 100644 --- a/test/txsim/run.go +++ b/test/txsim/run.go @@ -30,6 +30,7 @@ func Run( masterAccName string, seed int64, pollTime time.Duration, + useFeegrant bool, sequences ...Sequence, ) error { r := rand.New(rand.NewSource(seed)) @@ -46,14 +47,14 @@ func Run( defer queryClient.Close() // Create the account manager to handle account transactions. - manager, err := NewAccountManager(ctx, keys, masterAccName, txClient, queryClient) + manager, err := NewAccountManager(ctx, keys, masterAccName, txClient, queryClient, useFeegrant) if err != nil { return err } // Initiaize each of the sequences by allowing them to allocate accounts. for _, sequence := range sequences { - sequence.Init(ctx, manager.query.Conn(), manager.AllocateAccounts, r) + sequence.Init(ctx, manager.query.Conn(), manager.AllocateAccounts, r, useFeegrant) } // Generate the allotted accounts on chain by sending them sufficient funds diff --git a/test/txsim/run_test.go b/test/txsim/run_test.go index 5695a4bbff..b56a57f358 100644 --- a/test/txsim/run_test.go +++ b/test/txsim/run_test.go @@ -30,6 +30,7 @@ func TestTxSimulator(t *testing.T) { name string sequences []txsim.Sequence expMessages map[string]int64 + useFeegrant bool }{ { name: "send sequence", @@ -77,6 +78,20 @@ func TestTxSimulator(t *testing.T) { sdk.MsgTypeURL(&blob.MsgPayForBlobs{}): 10, }, }, + { + name: "multi mixed sequence using feegrant", + sequences: append(append( + txsim.NewSendSequence(2, 1000, 100).Clone(3), + txsim.NewStakeSequence(1000).Clone(3)...), + txsim.NewBlobSequence(txsim.NewRange(1000, 1000), txsim.NewRange(1, 3)).Clone(3)...), + expMessages: map[string]int64{ + sdk.MsgTypeURL(&bank.MsgSend{}): 15, + sdk.MsgTypeURL(&staking.MsgDelegate{}): 2, + sdk.MsgTypeURL(&distribution.MsgWithdrawDelegatorReward{}): 10, + sdk.MsgTypeURL(&blob.MsgPayForBlobs{}): 10, + }, + useFeegrant: true, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -93,6 +108,7 @@ func TestTxSimulator(t *testing.T) { "", 9001, time.Second, + tc.useFeegrant, tc.sequences..., ) // Expect all sequences to run for at least 30 seconds without error diff --git a/test/txsim/send.go b/test/txsim/send.go index 40b53e0b8d..155c02364a 100644 --- a/test/txsim/send.go +++ b/test/txsim/send.go @@ -13,8 +13,9 @@ import ( var _ Sequence = &SendSequence{} const ( - SendGasLimit = 100000 - sendFee = SendGasLimit * appconsts.DefaultMinGasPrice + SendGasLimit = 100000 + FeegrantGasLimit = 800000 + sendFee = SendGasLimit * appconsts.DefaultMinGasPrice ) // SendSequence sets up an endless sequence of send transactions, moving tokens @@ -47,7 +48,7 @@ func (s *SendSequence) Clone(n int) []Sequence { // Init sets up the accounts involved in the sequence. It calculates the necessary balance as the fees per transaction // multiplied by the number of expected iterations plus the amount to be sent from one account to another -func (s *SendSequence) Init(_ context.Context, _ grpc.ClientConn, allocateAccounts AccountAllocator, _ *rand.Rand) { +func (s *SendSequence) Init(_ context.Context, _ grpc.ClientConn, allocateAccounts AccountAllocator, _ *rand.Rand, _ bool) { amount := s.sendAmount + (s.numIterations * int(sendFee)) s.accounts = allocateAccounts(s.numAccounts, amount) } diff --git a/test/txsim/sequence.go b/test/txsim/sequence.go index b3206fe73b..b272bab4b8 100644 --- a/test/txsim/sequence.go +++ b/test/txsim/sequence.go @@ -21,7 +21,7 @@ type Sequence interface { // Init allows the sequence to initialize itself. It may read the current state of // the chain and provision accounts for usage throughout the sequence. // For any randomness, use the rand source provided. - Init(ctx context.Context, querier grpc.ClientConn, accountAllocator AccountAllocator, rand *rand.Rand) + Init(ctx context.Context, querier grpc.ClientConn, accountAllocator AccountAllocator, rand *rand.Rand, useFeegrant bool) // Next returns the next operation in the sequence. It returns EndOfSequence // when the sequence has been exhausted. The sequence may make use of the diff --git a/test/txsim/stake.go b/test/txsim/stake.go index 5e35054481..c7d7fb3353 100644 --- a/test/txsim/stake.go +++ b/test/txsim/stake.go @@ -38,8 +38,12 @@ func (s *StakeSequence) Clone(n int) []Sequence { return sequenceGroup } -func (s *StakeSequence) Init(_ context.Context, _ grpc.ClientConn, allocateAccounts AccountAllocator, _ *rand.Rand) { - s.account = allocateAccounts(1, s.initialStake+fundsForGas)[0] +func (s *StakeSequence) Init(_ context.Context, _ grpc.ClientConn, allocateAccounts AccountAllocator, _ *rand.Rand, useFeegrant bool) { + funds := fundsForGas + if useFeegrant { + funds = 1 + } + s.account = allocateAccounts(1, s.initialStake+funds)[0] } func (s *StakeSequence) Next(ctx context.Context, querier grpc.ClientConn, rand *rand.Rand) (Operation, error) {