From 5826c7eccaeb202e184865e9030adc02b7747304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Negovanovi=C4=87?= <93934272+Stefan-Ethernal@users.noreply.github.com> Date: Fri, 5 May 2023 09:50:46 +0200 Subject: [PATCH] Parallelize bootstrap rootchain CLI commands (#1468) * Paralelize fund command * Paralelize rootchain contract deployment * Parallelize rootchain contracts initialization * Decrease timeout to 1h --- Makefile | 2 +- command/rootchain/deploy/deploy.go | 157 +++++++++++++++------ command/rootchain/deploy/deploy_test.go | 3 +- command/rootchain/fund/fund.go | 176 +++++++++++------------- command/rootchain/fund/params.go | 93 +++---------- consensus/polybft/README.md | 4 +- e2e-polybft/e2e/consensus_test.go | 6 +- e2e-polybft/framework/test-bridge.go | 45 ++++-- e2e-polybft/framework/test-server.go | 17 ++- types/types.go | 20 +++ 10 files changed, 291 insertions(+), 232 deletions(-) diff --git a/Makefile b/Makefile index 0b3816081b..001100e9e9 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ test-e2e-polybft: # We can not build with race because of a bug in boltdb dependency go build -o artifacts/polygon-edge . env EDGE_BINARY=${PWD}/artifacts/polygon-edge E2E_TESTS=true E2E_LOGS=true \ - go test -v -timeout=1h30m ./e2e-polybft/e2e/... + go test -v -timeout=1h ./e2e-polybft/e2e/... .PHONY: test-property-polybft test-property-polybft: diff --git a/command/rootchain/deploy/deploy.go b/command/rootchain/deploy/deploy.go index 5d530c5824..788055a0d5 100644 --- a/command/rootchain/deploy/deploy.go +++ b/command/rootchain/deploy/deploy.go @@ -1,6 +1,7 @@ package deploy import ( + "context" "errors" "fmt" "math/big" @@ -8,6 +9,7 @@ import ( "github.com/spf13/cobra" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/jsonrpc" + "golang.org/x/sync/errgroup" "github.com/0xPolygon/polygon-edge/chain" "github.com/0xPolygon/polygon-edge/command" @@ -98,6 +100,53 @@ var ( rootchainConfig.StakeManagerAddress = addr }, } + + // initializersMap maps rootchain contract names to initializer function callbacks + initializersMap = map[string]func(command.OutputFormatter, txrelayer.TxRelayer, + *polybft.RootchainConfig, ethgo.Key) error{ + stakeManagerName: func(fmt command.OutputFormatter, + relayer txrelayer.TxRelayer, + config *polybft.RootchainConfig, + key ethgo.Key) error { + + return initializeStakeManager(fmt, relayer, config, key) + }, + customSupernetManagerName: func(fmt command.OutputFormatter, + relayer txrelayer.TxRelayer, + config *polybft.RootchainConfig, + key ethgo.Key) error { + + return initializeSupernetManager(fmt, relayer, config, key) + }, + exitHelperName: func(fmt command.OutputFormatter, + relayer txrelayer.TxRelayer, + config *polybft.RootchainConfig, + key ethgo.Key) error { + + return initializeExitHelper(fmt, relayer, config, key) + }, + rootERC20PredicateName: func(fmt command.OutputFormatter, + relayer txrelayer.TxRelayer, + config *polybft.RootchainConfig, + key ethgo.Key) error { + + return initializeRootERC20Predicate(fmt, relayer, config, key) + }, + rootERC721PredicateName: func(fmt command.OutputFormatter, + relayer txrelayer.TxRelayer, + config *polybft.RootchainConfig, + key ethgo.Key) error { + + return initializeRootERC721Predicate(fmt, relayer, config, key) + }, + rootERC1155PredicateName: func(fmt command.OutputFormatter, + relayer txrelayer.TxRelayer, + config *polybft.RootchainConfig, + key ethgo.Key) error { + + return initializeRootERC1155Predicate(fmt, relayer, config, key) + }, + } ) // GetCommand returns the rootchain deploy command @@ -213,7 +262,7 @@ func runCommand(cmd *cobra.Command, _ []string) { } } - rootchainCfg, chainID, err := deployContracts(outputter, client, consensusConfig.InitialValidatorSet) + rootchainCfg, chainID, err := deployContracts(outputter, client, consensusConfig.InitialValidatorSet, cmd.Context()) if err != nil { outputter.SetError(fmt.Errorf("failed to deploy rootchain contracts: %w", err)) @@ -253,8 +302,7 @@ func runCommand(cmd *cobra.Command, _ []string) { // deployContracts deploys and initializes rootchain smart contracts func deployContracts(outputter command.OutputFormatter, client *jsonrpc.Client, - initialValidators []*polybft.Validator) (*polybft.RootchainConfig, int64, error) { - // if the bridge contract is not created, we have to deploy all the contracts + initialValidators []*polybft.Validator, cmdCtx context.Context) (*polybft.RootchainConfig, int64, error) { txRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithClient(client)) if err != nil { return nil, 0, fmt.Errorf("failed to initialize tx relayer: %w", err) @@ -378,43 +426,82 @@ func deployContracts(outputter command.OutputFormatter, client *jsonrpc.Client, allContracts = append(tokenContracts, allContracts...) - for _, contract := range allContracts { - txn := ðgo.Transaction{ - To: nil, // contract deployment - Input: contract.artifact.Bytecode, - } + g, ctx := errgroup.WithContext(cmdCtx) + resultsCh := make(chan *deployContractResult, len(allContracts)) - receipt, err := txRelayer.SendTransaction(txn, deployerKey) - if err != nil { - return nil, 0, err - } + for _, contract := range allContracts { + contract := contract + + g.Go(func() error { + select { + case <-ctx.Done(): + return ctx.Err() + default: + txn := ðgo.Transaction{ + To: nil, // contract deployment + Input: contract.artifact.Bytecode, + } + + receipt, err := txRelayer.SendTransaction(txn, deployerKey) + if err != nil { + return err + } + + if receipt == nil || receipt.Status != uint64(types.ReceiptSuccess) { + return fmt.Errorf("deployment of %s contract failed", contract.name) + } + + resultsCh <- newDeployContractsResult(contract.name, + types.Address(receipt.ContractAddress), + receipt.TransactionHash) + + return nil + } + }) + } - if receipt == nil || receipt.Status != uint64(types.ReceiptSuccess) { - return nil, 0, fmt.Errorf("deployment of %s contract failed", contract.name) - } + if err := g.Wait(); err != nil { + return nil, 0, err + } - contractAddr := types.Address(receipt.ContractAddress) + close(resultsCh) - populatorFn, ok := metadataPopulatorMap[contract.name] + for result := range resultsCh { + populatorFn, ok := metadataPopulatorMap[result.Name] if !ok { - return nil, 0, fmt.Errorf("rootchain metadata populator not registered for contract '%s'", contract.name) + return nil, 0, fmt.Errorf("rootchain metadata populator not registered for contract '%s'", result.Name) } - populatorFn(rootchainConfig, contractAddr) + populatorFn(rootchainConfig, result.Address) - outputter.WriteCommandResult(newDeployContractsResult(contract.name, contractAddr, receipt.TransactionHash)) + outputter.WriteCommandResult(result) } - // init StakeManager - if err := initializeStakeManager(outputter, txRelayer, rootchainConfig, deployerKey); err != nil { - return nil, 0, err + g, ctx = errgroup.WithContext(cmdCtx) + + for _, contract := range allContracts { + contract := contract + + initializer, exists := initializersMap[contract.name] + if !exists { + continue + } + + g.Go(func() error { + select { + case <-cmdCtx.Done(): + return cmdCtx.Err() + default: + return initializer(outputter, txRelayer, rootchainConfig, deployerKey) + } + }) } - // init SupernetManager - if err := initializeSupernetManager(outputter, txRelayer, rootchainConfig, deployerKey); err != nil { + if err := g.Wait(); err != nil { return nil, 0, err } + // register supernets manager on stake manager chainID, err := registerChainOnStakeManager(txRelayer, rootchainConfig, deployerKey) if err != nil { return nil, 0, err @@ -426,26 +513,6 @@ func deployContracts(outputter command.OutputFormatter, client *jsonrpc.Client, return nil, 0, err } - // init ExitHelper - if err := initializeExitHelper(outputter, txRelayer, rootchainConfig, deployerKey); err != nil { - return nil, 0, err - } - - // init RootERC20Predicate - if err := initializeRootERC20Predicate(outputter, txRelayer, rootchainConfig, deployerKey); err != nil { - return nil, 0, err - } - - // init RootERC721Predicate - if err := initializeRootERC721Predicate(outputter, txRelayer, rootchainConfig, deployerKey); err != nil { - return nil, 0, err - } - - // init RootERC1155Predicate - if err := initializeRootERC1155Predicate(outputter, txRelayer, rootchainConfig, deployerKey); err != nil { - return rootchainConfig, 0, err - } - return rootchainConfig, chainID, nil } diff --git a/command/rootchain/deploy/deploy_test.go b/command/rootchain/deploy/deploy_test.go index 6b96ffc34d..8127348c1d 100644 --- a/command/rootchain/deploy/deploy_test.go +++ b/command/rootchain/deploy/deploy_test.go @@ -1,6 +1,7 @@ package deploy import ( + "context" "os" "testing" @@ -37,7 +38,7 @@ func TestDeployContracts_NoPanics(t *testing.T) { outputter := command.InitializeOutputter(GetCommand()) require.NotPanics(t, func() { - _, _, err = deployContracts(outputter, client, []*polybft.Validator{}) + _, _, err = deployContracts(outputter, client, []*polybft.Validator{}, context.Background()) }) require.NoError(t, err) } diff --git a/command/rootchain/fund/fund.go b/command/rootchain/fund/fund.go index 6359d15faf..f61080b60a 100644 --- a/command/rootchain/fund/fund.go +++ b/command/rootchain/fund/fund.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/cobra" "github.com/umbracle/ethgo" + "golang.org/x/sync/errgroup" "github.com/0xPolygon/polygon-edge/command" "github.com/0xPolygon/polygon-edge/command/polybftsecrets" @@ -32,25 +33,18 @@ func GetCommand() *cobra.Command { } func setFlags(cmd *cobra.Command) { - cmd.Flags().StringVar( - ¶ms.dataDir, - polybftsecrets.AccountDirFlag, - "", - polybftsecrets.AccountDirFlagDesc, - ) - - cmd.Flags().StringVar( - ¶ms.configPath, - polybftsecrets.AccountConfigFlag, - "", - polybftsecrets.AccountConfigFlagDesc, + cmd.Flags().StringSliceVar( + ¶ms.addresses, + addressesFlag, + nil, + "validator addresses", ) - cmd.Flags().StringVar( - ¶ms.amount, - amountFlag, - "", - "tokens amount which is funded to validator on a root chain", + cmd.Flags().StringSliceVar( + ¶ms.amounts, + amountsFlag, + nil, + "token amounts which is funded to validator on a root chain", ) cmd.Flags().StringVar( @@ -80,10 +74,6 @@ func setFlags(cmd *cobra.Command) { "", polybftsecrets.PrivateKeyFlagDesc, ) - - // Don't accept data-dir and config flags because they are related to different secrets managers. - // data-dir is about the local FS as secrets storage, config is about remote secrets manager. - cmd.MarkFlagsMutuallyExclusive(polybftsecrets.AccountDirFlag, polybftsecrets.AccountConfigFlag) } func preRunCommand(_ *cobra.Command, _ []string) error { @@ -101,91 +91,91 @@ func runCommand(cmd *cobra.Command, _ []string) { return } - if err := params.initSecretsManager(); err != nil { - outputter.SetError(err) - - return - } - - validatorAddr, err := params.getValidatorAccount() - if err != nil { - outputter.SetError(err) + var ( + deployerKey ethgo.Key + rootTokenAddr types.Address + ) - return - } + if params.mintRootToken { + deployerKey, err = helper.GetRootchainPrivateKey(params.deployerPrivateKey) + if err != nil { + outputter.SetError(fmt.Errorf("failed to initialize deployer private key: %w", err)) - gasPrice, err := txRelayer.Client().Eth().GasPrice() - if err != nil { - outputter.SetError(err) + return + } - return + rootTokenAddr = types.StringToAddress(params.nativeRootTokenAddr) } - fundAddr := ethgo.Address(validatorAddr) - txn := ðgo.Transaction{ - To: &fundAddr, - Value: params.amountValue, - GasPrice: gasPrice, + cmdResults := make(chan command.CommandResult, len(params.addresses)) + g, ctx := errgroup.WithContext(cmd.Context()) + + for i := 0; i < len(params.addresses); i++ { + i := i + + g.Go(func() error { + select { + case <-ctx.Done(): + return ctx.Err() + + default: + validatorAddr := types.StringToAddress(params.addresses[i]) + fundAddr := ethgo.Address(validatorAddr) + txn := ðgo.Transaction{ + To: &fundAddr, + Value: params.amountValues[i], + } + + receipt, err := txRelayer.SendTransactionLocal(txn) + if err != nil { + return fmt.Errorf("failed to send fund validator '%s' transaction: %w", validatorAddr, err) + } + + if receipt.Status == uint64(types.ReceiptFailed) { + return fmt.Errorf("failed to fund validator '%s'", validatorAddr) + } + + if params.mintRootToken { + // mint tokens to validator, so he is able to send them + mintTxn, err := helper.CreateMintTxn(validatorAddr, rootTokenAddr, params.amountValues[i]) + if err != nil { + return fmt.Errorf("failed to create mint native tokens transaction for validator '%s'. err: %w", + validatorAddr, err) + } + + receipt, err := txRelayer.SendTransaction(mintTxn, deployerKey) + if err != nil { + return fmt.Errorf("failed to send mint native tokens transaction to validator '%s'. err: %w", validatorAddr, err) + } + + if receipt.Status == uint64(types.ReceiptFailed) { + return fmt.Errorf("failed to mint native tokens to validator '%s'", validatorAddr) + } + } + + cmdResults <- &result{ + ValidatorAddr: validatorAddr, + TxHash: types.Hash(receipt.TransactionHash), + IsMinted: params.mintRootToken, + } + } + + return nil + }) } - receipt, err := txRelayer.SendTransactionLocal(txn) - if err != nil { + if err := g.Wait(); err != nil { outputter.SetError(err) return } - if receipt.Status == uint64(types.ReceiptFailed) { - _, _ = outputter.Write([]byte(fmt.Sprintf("failed to fund validator '%s'", validatorAddr.String()))) - - return - } - - result := &result{ - ValidatorAddr: validatorAddr, - TxHash: types.Hash(receipt.TransactionHash), - } - - if params.mintRootToken { - depositorKey, err := helper.GetRootchainPrivateKey(params.deployerPrivateKey) - if err != nil { - outputter.SetError(fmt.Errorf("failed to initialize depositor private key: %w", err)) - - return - } - - rootTokenAddr := types.StringToAddress(params.nativeRootTokenAddr) - - // mint tokens to validator, so he is able to send them - mintTxn, err := helper.CreateMintTxn(validatorAddr, rootTokenAddr, params.amountValue) - if err != nil { - outputter.SetError(fmt.Errorf("mint transaction creation failed for validator: %s. err: %w", validatorAddr, err)) - - return - } - - mintTxn.GasPrice, err = txRelayer.Client().Eth().GasPrice() - if err != nil { - outputter.SetError(err) - - return - } - - receipt, err := txRelayer.SendTransaction(mintTxn, depositorKey) - if err != nil { - outputter.SetError(fmt.Errorf("failed to send mint transaction to depositor %s. err: %w", validatorAddr, err)) - - return - } - - if receipt.Status == uint64(types.ReceiptFailed) { - outputter.SetError(fmt.Errorf("failed to mint tokens to depositor %s", validatorAddr)) - - return - } + close(cmdResults) - result.IsMinted = true + results := []command.CommandResult{} + for result := range cmdResults { + results = append(results, result) } - outputter.SetCommandResult(command.Results([]command.CommandResult{result})) + outputter.SetCommandResult(command.Results(results)) } diff --git a/command/rootchain/fund/params.go b/command/rootchain/fund/params.go index b6582a33d3..749c35132f 100644 --- a/command/rootchain/fund/params.go +++ b/command/rootchain/fund/params.go @@ -2,112 +2,59 @@ package fund import ( "errors" - "fmt" "math/big" - "os" cmdhelper "github.com/0xPolygon/polygon-edge/command/helper" - "github.com/0xPolygon/polygon-edge/secrets" - "github.com/0xPolygon/polygon-edge/secrets/helper" "github.com/0xPolygon/polygon-edge/types" ) const ( - amountFlag = "amount" + addressesFlag = "addresses" + amountsFlag = "amounts" jsonRPCFlag = "json-rpc" mintRootTokenFlag = "mint" ) var ( - errInvalidConfig = errors.New("invalid secrets configuration") - errInvalidParams = errors.New("no config file or data directory passed in") - errUnsupportedType = errors.New("unsupported secrets manager") + errNoAddressesProvided = errors.New("no addresses provided") + errInconsistentLength = errors.New("validator addresses and amounts must be equal length") ) type fundParams struct { - dataDir string - configPath string - amount string + addresses []string + amounts []string nativeRootTokenAddr string deployerPrivateKey string mintRootToken bool jsonRPCAddress string - secretsManager secrets.SecretsManager - secretsConfig *secrets.SecretsManagerConfig - - amountValue *big.Int + amountValues []*big.Int } -func (fp *fundParams) validateFlags() (err error) { - if fp.amountValue, err = cmdhelper.ParseAmount(fp.amount); err != nil { - return err - } - - if fp.dataDir == "" && fp.configPath == "" { - return errInvalidParams +func (fp *fundParams) validateFlags() error { + if len(fp.addresses) == 0 { + return errNoAddressesProvided } - if fp.dataDir != "" { - if _, err := os.Stat(fp.dataDir); err != nil { - return fmt.Errorf("invalid validators secrets path ('%s') provided. Error: %w", fp.dataDir, err) - } + if len(fp.amounts) != len(fp.addresses) { + return errInconsistentLength } - if fp.configPath != "" { - if _, err := os.Stat(fp.configPath); err != nil { - return fmt.Errorf("invalid validators secrets config path ('%s') provided. Error: %w", fp.configPath, err) + for _, addr := range fp.addresses { + if err := types.IsValidAddress(addr); err != nil { + return err } } - return nil -} - -func (fp *fundParams) hasConfigPath() bool { - return fp.configPath != "" -} - -func (fp *fundParams) initSecretsManager() error { - var err error - if fp.hasConfigPath() { - if err = fp.parseConfig(); err != nil { + fp.amountValues = make([]*big.Int, len(fp.amounts)) + for i, amountRaw := range fp.amounts { + amountValue, err := cmdhelper.ParseAmount(amountRaw) + if err != nil { return err } - fp.secretsManager, err = helper.InitCloudSecretsManager(fp.secretsConfig) - - return err - } - - return fp.initLocalSecretsManager() -} - -func (fp *fundParams) parseConfig() error { - secretsConfig, readErr := secrets.ReadConfig(fp.configPath) - if readErr != nil { - return errInvalidConfig - } - - if !secrets.SupportedServiceManager(secretsConfig.Type) { - return errUnsupportedType + fp.amountValues[i] = amountValue } - fp.secretsConfig = secretsConfig - return nil } - -func (fp *fundParams) initLocalSecretsManager() error { - local, err := helper.SetupLocalSecretsManager(fp.dataDir) - if err != nil { - return err - } - - fp.secretsManager = local - - return nil -} - -func (fp *fundParams) getValidatorAccount() (types.Address, error) { - return helper.LoadValidatorAddress(fp.secretsManager) -} diff --git a/consensus/polybft/README.md b/consensus/polybft/README.md index 1b400137c4..60b002f41f 100644 --- a/consensus/polybft/README.md +++ b/consensus/polybft/README.md @@ -61,7 +61,9 @@ It has a native support for running bridge, which enables running cross-chain tr 6. Fund validators on rootchain - in order for validators to be able to send transactions to Ethereum, they need to be funded in order to be able to cover gas cost. **This command is for testing purposes only.** ```bash - $ polygon-edge rootchain fund --data-dir ./test-chain-1 + $ polygon-edge rootchain fund \ + --addresses 0x1234567890123456789012345678901234567890 \ + --amounts 200000000000000000000 ``` 7. Whitelist validators on rootchain - in order for validators to be able to be registered on the SupernetManager contract on rootchain. Note that only deployer of SupernetManager contract (the one who run the deploy command) can whitelist validators on rootchain. He can use either its hex encoded private key, or data-dir flag if he has secerets initialized: diff --git a/e2e-polybft/e2e/consensus_test.go b/e2e-polybft/e2e/consensus_test.go index 9b0685413a..dd4c2770f8 100644 --- a/e2e-polybft/e2e/consensus_test.go +++ b/e2e-polybft/e2e/consensus_test.go @@ -178,11 +178,13 @@ func TestE2E_Consensus_RegisterValidator(t *testing.T) { initialBalance := ethgo.Ether(500) // fund first new validator - err = cluster.Bridge.FundSingleValidator(polybftConfig.Bridge.RootNativeERC20Addr, path.Join(cluster.Config.TmpDir, firstValidatorSecrets), initialBalance) + err = cluster.Bridge.FundValidators(polybftConfig.Bridge.RootNativeERC20Addr, + []string{path.Join(cluster.Config.TmpDir, firstValidatorSecrets)}, []*big.Int{initialBalance}) require.NoError(t, err) // fund second new validator - err = cluster.Bridge.FundSingleValidator(polybftConfig.Bridge.RootNativeERC20Addr, path.Join(cluster.Config.TmpDir, secondValidatorSecrets), initialBalance) + err = cluster.Bridge.FundValidators(polybftConfig.Bridge.RootNativeERC20Addr, + []string{path.Join(cluster.Config.TmpDir, secondValidatorSecrets)}, []*big.Int{initialBalance}) require.NoError(t, err) // first validator's balance to be received diff --git a/e2e-polybft/framework/test-bridge.go b/e2e-polybft/framework/test-bridge.go index c83718d732..b7475a5644 100644 --- a/e2e-polybft/framework/test-bridge.go +++ b/e2e-polybft/framework/test-bridge.go @@ -17,6 +17,7 @@ import ( rootHelper "github.com/0xPolygon/polygon-edge/command/rootchain/helper" "github.com/0xPolygon/polygon-edge/command/rootchain/server" "github.com/0xPolygon/polygon-edge/consensus/polybft" + "github.com/0xPolygon/polygon-edge/consensus/polybft/wallet" "github.com/0xPolygon/polygon-edge/types" "golang.org/x/sync/errgroup" ) @@ -291,14 +292,17 @@ func (t *TestBridge) fundRootchainValidators(polybftConfig polybft.PolyBFTConfig return fmt.Errorf("could not get validator secrets on initial rootchain funding of genesis validators: %w", err) } + balances := make([]*big.Int, len(polybftConfig.InitialValidatorSet)) + secrets := make([]string, len(validatorSecrets)) + for i, secret := range validatorSecrets { - err := t.FundSingleValidator( - polybftConfig.Bridge.RootNativeERC20Addr, - path.Join(t.clusterConfig.TmpDir, secret), - polybftConfig.InitialValidatorSet[i].Balance) - if err != nil { - return fmt.Errorf("failed to fund validators on the rootchain: %w", err) - } + secrets[i] = path.Join(t.clusterConfig.TmpDir, secret) + balances[i] = polybftConfig.InitialValidatorSet[i].Balance + } + + if err := t.FundValidators(polybftConfig.Bridge.RootNativeERC20Addr, + secrets, balances); err != nil { + return fmt.Errorf("failed to fund validators on the rootchain: %w", err) } return nil @@ -424,19 +428,36 @@ func (t *TestBridge) finalizeGenesis(genesisPath string, polybftConfig polybft.P return nil } -// FundSingleValidator sends tokens to a rootchain validators -func (t *TestBridge) FundSingleValidator(tookenAddress types.Address, secretsPath string, amount *big.Int) error { +// FundValidators sends tokens to a rootchain validators +func (t *TestBridge) FundValidators(tookenAddress types.Address, secretsPaths []string, amounts []*big.Int) error { + if len(secretsPaths) != len(amounts) { + return errors.New("expected the same length of secrets paths and amounts") + } + args := []string{ "rootchain", "fund", - "--" + polybftsecrets.AccountDirFlag, secretsPath, - "--amount", amount.String(), "--native-root-token", tookenAddress.String(), "--mint", } + for i := 0; i < len(secretsPaths); i++ { + secretsManager, err := polybftsecrets.GetSecretsManager(secretsPaths[i], "", true) + if err != nil { + return err + } + + key, err := wallet.GetEcdsaFromSecret(secretsManager) + if err != nil { + return err + } + + args = append(args, "--addresses", key.Address().String()) + args = append(args, "--amounts", amounts[i].String()) + } + if err := t.cmdRun(args...); err != nil { - return fmt.Errorf("failed to fund a validator on the rootchain: %w", err) + return err } return nil diff --git a/e2e-polybft/framework/test-server.go b/e2e-polybft/framework/test-server.go index 254833fe23..8dc94ab994 100644 --- a/e2e-polybft/framework/test-server.go +++ b/e2e-polybft/framework/test-server.go @@ -12,11 +12,12 @@ import ( "github.com/0xPolygon/polygon-edge/command/polybftsecrets" rootHelper "github.com/0xPolygon/polygon-edge/command/rootchain/helper" "github.com/0xPolygon/polygon-edge/consensus/polybft" + "github.com/0xPolygon/polygon-edge/consensus/polybft/wallet" "github.com/0xPolygon/polygon-edge/server/proto" txpoolProto "github.com/0xPolygon/polygon-edge/txpool/proto" "github.com/0xPolygon/polygon-edge/types" "github.com/google/uuid" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/jsonrpc" "google.golang.org/grpc" @@ -49,6 +50,7 @@ func getOpenPortForServer() int64 { type TestServer struct { t *testing.T + address types.Address clusterConfig *TestClusterConfig config *TestServerConfig node *node @@ -115,14 +117,21 @@ func NewTestServer(t *testing.T, clusterConfig *TestClusterConfig, if config.DataDir == "" { dataDir, err := ioutil.TempDir("/tmp", "edge-e2e-") - assert.NoError(t, err) + require.NoError(t, err) config.DataDir = dataDir } + secretsManager, err := polybftsecrets.GetSecretsManager(config.DataDir, "", true) + require.NoError(t, err) + + key, err := wallet.GetEcdsaFromSecret(secretsManager) + require.NoError(t, err) + srv := &TestServer{ t: t, clusterConfig: clusterConfig, + address: types.Address(key.Address()), config: config, } srv.Start() @@ -192,10 +201,10 @@ func (t *TestServer) RootchainFund(rootNativeERC20Addr types.Address, amount *bi args := []string{ "rootchain", "fund", - "--" + polybftsecrets.AccountDirFlag, t.DataDir(), + "--addresses", t.address.String(), + "--amounts", amount.String(), "--json-rpc", t.BridgeJSONRPCAddr(), "--native-root-token", rootNativeERC20Addr.String(), - "--amount", amount.String(), "--mint", } diff --git a/types/types.go b/types/types.go index d88e8bcc4f..6a5faded4b 100644 --- a/types/types.go +++ b/types/types.go @@ -133,6 +133,26 @@ func StringToBytes(str string) []byte { return b } +// IsValidAddress checks if provided string is a valid Ethereum address +func IsValidAddress(address string) error { + // remove 0x prefix if it exists + if strings.HasPrefix(address, "0x") { + address = address[2:] + } + + // check if the address has the correct length + if len(address) != 2*AddressLength { + return fmt.Errorf("address %s has invalid length", address) + } + + // decode the address + if _, err := hex.DecodeString(address); err != nil { + return fmt.Errorf("address %s contains invalid characters", address) + } + + return nil +} + // UnmarshalText parses a hash in hex syntax. func (h *Hash) UnmarshalText(input []byte) error { *h = BytesToHash(StringToBytes(string(input)))