From 2d0b7d3fbb9fe269571715415d07dbfc3a652bab Mon Sep 17 00:00:00 2001 From: Evan Forbes <42654277+evan-forbes@users.noreply.github.com> Date: Thu, 17 Aug 2023 08:44:13 -0500 Subject: [PATCH 1/2] feat: add the ability to specifiy the txsim master account (#2292) ## Overview This PR adds the ability to specify a specific account to use as master for txsim. This is useful for when we don't want to always use the account with the most funds in our wallet. closes #2286 kinda sorta blocking #2197 ## Checklist - [x] New and updated code has appropriate documentation - [x] New and updated code has new and/or updated testing - [x] Required CI checks are passing - [x] Visual proof for any user facing features like CLI or documentation updates - [x] Linked issues closed with keywords (cherry picked from commit 21021891240f4f1bf18185947c3a95339837375f) # Conflicts: # test/e2e/simple_test.go --- app/test/square_size_test.go | 1 + test/cmd/txsim/cli.go | 31 +++++++++++-------- test/e2e/simple_test.go | 55 +++++++++++++++++++++++++++++++++ test/txsim/account.go | 60 +++++++++++++++++++++++++++++++----- test/txsim/run.go | 3 +- test/txsim/run_test.go | 1 + 6 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 test/e2e/simple_test.go diff --git a/app/test/square_size_test.go b/app/test/square_size_test.go index 43ee3d274c..1e69d9b720 100644 --- a/app/test/square_size_test.go +++ b/app/test/square_size_test.go @@ -136,6 +136,7 @@ func (s *SquareSizeIntegrationTest) fillBlocks(blobSize, blobsPerPFB, pfbsPerBlo []string{s.rpcAddr}, []string{s.grpcAddr}, s.cctx.Keyring, + "", rand.Int63(), time.Second, seqs..., diff --git a/test/cmd/txsim/cli.go b/test/cmd/txsim/cli.go index 52ff1d80aa..550b6beb49 100644 --- a/test/cmd/txsim/cli.go +++ b/test/cmd/txsim/cli.go @@ -22,22 +22,23 @@ import ( // A set of environment variables that can be used instead of flags const ( - TxsimGRPC = "TXSIM_GRPC" - TxsimRPC = "TXSIM_RPC" - TxsimSeed = "TXSIM_SEED" - TxsimPoll = "TXSIM_POLL" - TxsimKeypath = "TXSIM_KEYPATH" - TxsimMnemonic = "TXSIM_MNEMONIC" + TxsimGRPC = "TXSIM_GRPC" + TxsimRPC = "TXSIM_RPC" + TxsimSeed = "TXSIM_SEED" + TxsimPoll = "TXSIM_POLL" + TxsimKeypath = "TXSIM_KEYPATH" + TxsimMasterAccName = "TXSIM_MASTER_ACC_NAME" + TxsimMnemonic = "TXSIM_MNEMONIC" ) // Values for all flags var ( - keyPath, keyMnemonic, rpcEndpoints, grpcEndpoints string - blobSizes, blobAmounts string - seed int64 - pollTime time.Duration - send, sendIterations, sendAmount int - stake, stakeValue, blob int + keyPath, masterAccName, keyMnemonic, rpcEndpoints, grpcEndpoints string + blobSizes, blobAmounts string + seed int64 + pollTime time.Duration + send, sendIterations, sendAmount int + stake, stakeValue, blob int ) func main() { @@ -101,6 +102,10 @@ well funded account that can act as the master account. The command runs until a } } + if masterAccName == "" { + masterAccName = os.Getenv(TxsimMasterAccName) + } + if stake == 0 && send == 0 && blob == 0 { return errors.New("no sequences specified. Use --stake, --send or --blob") } @@ -154,6 +159,7 @@ well funded account that can act as the master account. The command runs until a strings.Split(rpcEndpoints, ","), strings.Split(grpcEndpoints, ","), keys, + masterAccName, seed, pollTime, sequences..., @@ -172,6 +178,7 @@ well funded account that can act as the master account. The command runs until a func flags() *flag.FlagSet { flags := &flag.FlagSet{} flags.StringVar(&keyPath, "key-path", "", "path to the keyring") + flags.StringVar(&masterAccName, "master", "", "the account name of the master account. Leaving empty will result in using the account with the most funds.") flags.StringVar(&keyMnemonic, "key-mnemonic", "", "space separated mnemonic for the keyring. The hdpath used is an empty string") flags.StringVar(&rpcEndpoints, "rpc-endpoints", "", "comma separated list of rpc endpoints") flags.StringVar(&grpcEndpoints, "grpc-endpoints", "", "comma separated list of grpc endpoints") diff --git a/test/e2e/simple_test.go b/test/e2e/simple_test.go new file mode 100644 index 0000000000..c3f47c3834 --- /dev/null +++ b/test/e2e/simple_test.go @@ -0,0 +1,55 @@ +package e2e + +import ( + "context" + "errors" + "os" + "testing" + "time" + + "github.com/celestiaorg/celestia-app/test/txsim" + "github.com/celestiaorg/celestia-app/test/util/testnode" + "github.com/stretchr/testify/require" +) + +const ( + latestVersion = "v1.0.0-rc9" + seed = 42 // the meaning of life +) + +// This test runs a simple testnet with 4 validators. It submits both MsgPayForBlobs +// and MsgSends over 30 seconds and then asserts that at least 10 transactions were +// committed. +func TestE2ESimple(t *testing.T) { + if os.Getenv("E2E") == "" { + t.Skip("skipping e2e test") + } + + testnet, err := New(t.Name(), seed) + require.NoError(t, err) + t.Cleanup(testnet.Cleanup) + require.NoError(t, testnet.CreateGenesisNodes(4, latestVersion, 10000000)) + + kr, err := testnet.CreateGenesisAccount("alice", 1e12) + require.NoError(t, err) + + require.NoError(t, testnet.Setup()) + require.NoError(t, testnet.Start()) + + sequences := txsim.NewBlobSequence(txsim.NewRange(200, 4000), txsim.NewRange(1, 3)).Clone(5) + sequences = append(sequences, txsim.NewSendSequence(4, 1000, 100).Clone(5)...) + + 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...) + require.True(t, errors.Is(err, context.DeadlineExceeded), err.Error()) + + blockchain, err := testnode.ReadBlockchain(context.Background(), testnet.Node(0).AddressRPC()) + require.NoError(t, err) + + totalTxs := 0 + for _, block := range blockchain { + totalTxs += len(block.Data.Txs) + } + require.Greater(t, totalTxs, 10) +} diff --git a/test/txsim/account.go b/test/txsim/account.go index 99e63fc78b..e99de5e7cc 100644 --- a/test/txsim/account.go +++ b/test/txsim/account.go @@ -43,7 +43,7 @@ type Account struct { Balance int64 } -func NewAccountManager(ctx context.Context, keys keyring.Keyring, txClient *TxClient, queryClient *QueryClient) (*AccountManager, error) { +func NewAccountManager(ctx context.Context, keys keyring.Keyring, masterAccName string, txClient *TxClient, queryClient *QueryClient) (*AccountManager, error) { records, err := keys.List() if err != nil { return nil, err @@ -61,8 +61,14 @@ func NewAccountManager(ctx context.Context, keys keyring.Keyring, txClient *TxCl query: queryClient, } - if err := am.setupMasterAccount(ctx); err != nil { - return nil, err + if masterAccName == "" { + if err := am.setupDefaultMasterAccount(ctx); err != nil { + return nil, err + } + } else { + if err := am.setupSpecifiedMasterAccount(ctx, masterAccName); err != nil { + return nil, err + } } log.Info(). @@ -74,10 +80,10 @@ func NewAccountManager(ctx context.Context, keys keyring.Keyring, txClient *TxCl return am, nil } -// setupMasterAccount loops through all accounts in the keyring and picks out the one with +// setupDefaultMasterAccount loops through all accounts in the keyring and picks out the one with // the highest balance as the master account. Accounts that don't yet exist on chain are // ignored. -func (am *AccountManager) setupMasterAccount(ctx context.Context) error { +func (am *AccountManager) setupDefaultMasterAccount(ctx context.Context) error { am.mtx.Lock() defer am.mtx.Unlock() @@ -127,6 +133,44 @@ func (am *AccountManager) setupMasterAccount(ctx context.Context) error { return nil } +func (am *AccountManager) setupSpecifiedMasterAccount(ctx context.Context, masterAccName string) error { + masterRecord, err := am.keys.Key(masterAccName) + if err != nil { + return fmt.Errorf("error getting specified master account %s: %w", masterAccName, err) + } + + masterAddress, err := masterRecord.GetAddress() + if err != nil { + return fmt.Errorf("error getting address for account %s: %w", masterAccName, err) + } + + // search for the account on chain + masterBalance, err := am.getBalance(ctx, masterAddress) + if err != nil { + return fmt.Errorf("error getting specified master account %s balance: %w", masterAccName, err) + } + + accountNumber, sequence, err := am.getAccountDetails(ctx, masterAddress) + if err != nil { + return fmt.Errorf("error getting account details for account %s: %w", masterRecord.Name, err) + } + + pk, err := masterRecord.GetPubKey() + if err != nil { + return fmt.Errorf("error getting public key for account %s: %w", masterRecord.Name, err) + } + + am.masterAccount = &Account{ + Address: masterAddress, + PubKey: pk, + Sequence: sequence, + AccountNumber: accountNumber, + Balance: masterBalance, + } + + return nil +} + // AllocateAccounts is used by sequences to specify the number of accounts // and the balance of each of those accounts. Not concurrently safe. func (am *AccountManager) AllocateAccounts(n, balance int) []types.AccAddress { @@ -221,8 +265,8 @@ func (am *AccountManager) Submit(ctx context.Context, op Operation) error { return nil } -// Generate the pending accounts by sending the adequate funds and setting up the feegrant permissions. -// This operation is not concurrently safe. +// Generate the pending accounts by sending the adequate funds. This operation +// is not concurrently safe. func (am *AccountManager) GenerateAccounts(ctx context.Context) error { if len(am.pending) == 0 { return nil @@ -232,7 +276,7 @@ func (am *AccountManager) GenerateAccounts(ctx context.Context) error { // 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") + return fmt.Errorf("master account has insufficient funds needed: %v", acc.Balance) } bankMsg := bank.NewMsgSend(am.masterAccount.Address, acc.Address, types.NewCoins(types.NewInt64Coin(app.BondDenom, acc.Balance))) diff --git a/test/txsim/run.go b/test/txsim/run.go index 7251058645..285538d38f 100644 --- a/test/txsim/run.go +++ b/test/txsim/run.go @@ -27,6 +27,7 @@ func Run( ctx context.Context, rpcEndpoints, grpcEndpoints []string, keys keyring.Keyring, + masterAccName string, seed int64, pollTime time.Duration, sequences ...Sequence, @@ -45,7 +46,7 @@ func Run( defer queryClient.Close() // Create the account manager to handle account transactions. - manager, err := NewAccountManager(ctx, keys, txClient, queryClient) + manager, err := NewAccountManager(ctx, keys, masterAccName, txClient, queryClient) if err != nil { return err } diff --git a/test/txsim/run_test.go b/test/txsim/run_test.go index 681504e3e6..f8fb034388 100644 --- a/test/txsim/run_test.go +++ b/test/txsim/run_test.go @@ -87,6 +87,7 @@ func TestTxSimulator(t *testing.T) { []string{rpcAddr}, []string{grpcAddr}, keyring, + "", 9001, time.Second, tc.sequences..., From 58cd8e3cf306836feb07afe8d2910cd346d8f25a Mon Sep 17 00:00:00 2001 From: evan-forbes Date: Thu, 17 Aug 2023 09:52:29 -0500 Subject: [PATCH 2/2] chore: fix mergify --- test/e2e/simple_test.go | 55 ----------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 test/e2e/simple_test.go diff --git a/test/e2e/simple_test.go b/test/e2e/simple_test.go deleted file mode 100644 index c3f47c3834..0000000000 --- a/test/e2e/simple_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package e2e - -import ( - "context" - "errors" - "os" - "testing" - "time" - - "github.com/celestiaorg/celestia-app/test/txsim" - "github.com/celestiaorg/celestia-app/test/util/testnode" - "github.com/stretchr/testify/require" -) - -const ( - latestVersion = "v1.0.0-rc9" - seed = 42 // the meaning of life -) - -// This test runs a simple testnet with 4 validators. It submits both MsgPayForBlobs -// and MsgSends over 30 seconds and then asserts that at least 10 transactions were -// committed. -func TestE2ESimple(t *testing.T) { - if os.Getenv("E2E") == "" { - t.Skip("skipping e2e test") - } - - testnet, err := New(t.Name(), seed) - require.NoError(t, err) - t.Cleanup(testnet.Cleanup) - require.NoError(t, testnet.CreateGenesisNodes(4, latestVersion, 10000000)) - - kr, err := testnet.CreateGenesisAccount("alice", 1e12) - require.NoError(t, err) - - require.NoError(t, testnet.Setup()) - require.NoError(t, testnet.Start()) - - sequences := txsim.NewBlobSequence(txsim.NewRange(200, 4000), txsim.NewRange(1, 3)).Clone(5) - sequences = append(sequences, txsim.NewSendSequence(4, 1000, 100).Clone(5)...) - - 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...) - require.True(t, errors.Is(err, context.DeadlineExceeded), err.Error()) - - blockchain, err := testnode.ReadBlockchain(context.Background(), testnet.Node(0).AddressRPC()) - require.NoError(t, err) - - totalTxs := 0 - for _, block := range blockchain { - totalTxs += len(block.Data.Txs) - } - require.Greater(t, totalTxs, 10) -}