Skip to content

Commit

Permalink
feat!: add feegranter option to txsim (#2291)
Browse files Browse the repository at this point in the history
## 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
  • Loading branch information
evan-forbes authored Aug 17, 2023
1 parent 023fcec commit 33536b0
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 24 deletions.
1 change: 1 addition & 0 deletions app/test/square_size_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func (s *SquareSizeIntegrationTest) fillBlocks(blobSize, blobsPerPFB, pfbsPerBlo
"",
rand.Int63(),
time.Second,
false,
seqs...,
)

Expand Down
3 changes: 3 additions & 0 deletions test/cmd/txsim/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var (
pollTime time.Duration
send, sendIterations, sendAmount int
stake, stakeValue, blob int
useFeegrant bool
)

func main() {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion test/e2e/simple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
42 changes: 34 additions & 8 deletions test/txsim/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -32,6 +33,7 @@ type AccountManager struct {
mtx sync.Mutex
masterAccount *Account
accounts map[string]*Account
useFeegrant bool
}

type Account struct {
Expand All @@ -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
Expand All @@ -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 == "" {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand Down
26 changes: 19 additions & 7 deletions test/txsim/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}

Expand All @@ -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
}
5 changes: 3 additions & 2 deletions test/txsim/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func Run(
masterAccName string,
seed int64,
pollTime time.Duration,
useFeegrant bool,
sequences ...Sequence,
) error {
r := rand.New(rand.NewSource(seed))
Expand All @@ -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
Expand Down
16 changes: 16 additions & 0 deletions test/txsim/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func TestTxSimulator(t *testing.T) {
name string
sequences []txsim.Sequence
expMessages map[string]int64
useFeegrant bool
}{
{
name: "send sequence",
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down
7 changes: 4 additions & 3 deletions test/txsim/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion test/txsim/sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions test/txsim/stake.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 33536b0

Please sign in to comment.