package generate import ( "errors" "fmt" "os" "path/filepath" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config" morphUtil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpcclient/gas" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/spf13/cobra" "github.com/spf13/viper" "golang.org/x/sync/errgroup" ) func AlphabetCreds(cmd *cobra.Command, _ []string) error { // alphabet size is not part of the config size, err := cmd.Flags().GetUint(morphUtil.AlphabetSizeFlag) if err != nil { return err } if size == 0 { return errors.New("size must be > 0") } if size > morphUtil.MaxAlphabetNodes { return morphUtil.ErrTooManyAlphabetNodes } v := viper.GetViper() walletDir := config.ResolveHomePath(viper.GetString(morphUtil.AlphabetWalletsFlag)) pwds, err := initializeWallets(v, walletDir, int(size)) if err != nil { return err } _, err = morphUtil.InitializeContractWallet(v, walletDir) if err != nil { return err } cmd.Println("size:", size) cmd.Println("alphabet-wallets:", walletDir) for i := range pwds { cmd.Printf("wallet[%d]: %s\n", i, pwds[i]) } return nil } func initializeWallets(v *viper.Viper, walletDir string, size int) ([]string, error) { wallets := make([]*wallet.Wallet, size) pubs := make(keys.PublicKeys, size) passwords := make([]string, size) for i := range wallets { password, err := config.GetPassword(v, innerring.GlagoliticLetter(i).String()) if err != nil { return nil, fmt.Errorf("can't fetch password: %w", err) } p := filepath.Join(walletDir, innerring.GlagoliticLetter(i).String()+".json") f, err := os.OpenFile(p, os.O_CREATE, 0o644) if err != nil { return nil, fmt.Errorf("can't create wallet file: %w", err) } if err := f.Close(); err != nil { return nil, fmt.Errorf("can't close wallet file: %w", err) } w, err := wallet.NewWallet(p) if err != nil { return nil, fmt.Errorf("can't create wallet: %w", err) } if err := w.CreateAccount(morphUtil.SingleAccountName, password); err != nil { return nil, fmt.Errorf("can't create account: %w", err) } passwords[i] = password wallets[i] = w pubs[i] = w.Accounts[0].PrivateKey().PublicKey() } var errG errgroup.Group // Create committee account with N/2+1 multi-signature. majCount := smartcontract.GetMajorityHonestNodeCount(size) // Create consensus account with 2*N/3+1 multi-signature. bftCount := smartcontract.GetDefaultHonestNodeCount(size) for i := range wallets { i := i ps := pubs.Copy() errG.Go(func() error { if err := addMultisigAccount(wallets[i], majCount, morphUtil.CommitteeAccountName, passwords[i], ps); err != nil { return fmt.Errorf("can't create committee account: %w", err) } if err := addMultisigAccount(wallets[i], bftCount, morphUtil.ConsensusAccountName, passwords[i], ps); err != nil { return fmt.Errorf("can't create consentus account: %w", err) } if err := wallets[i].SavePretty(); err != nil { return fmt.Errorf("can't save wallet: %w", err) } return nil }) } if err := errG.Wait(); err != nil { return nil, err } return passwords, nil } func addMultisigAccount(w *wallet.Wallet, m int, name, password string, pubs keys.PublicKeys) error { acc := wallet.NewAccountFromPrivateKey(w.Accounts[0].PrivateKey()) acc.Label = name if err := acc.ConvertMultisig(m, pubs); err != nil { return err } if err := acc.Encrypt(password, keys.NEP2ScryptParams()); err != nil { return err } w.AddAccount(acc) return nil } func generateStorageCreds(cmd *cobra.Command, _ []string) error { return refillGas(cmd, storageGasConfigFlag, true) } func refillGas(cmd *cobra.Command, gasFlag string, createWallet bool) (err error) { // storage wallet path is not part of the config storageWalletPath, _ := cmd.Flags().GetString(morphUtil.StorageWalletFlag) // wallet address is not part of the config walletAddress, _ := cmd.Flags().GetString(walletAddressFlag) var gasReceiver util.Uint160 if len(walletAddress) != 0 { gasReceiver, err = address.StringToUint160(walletAddress) if err != nil { return fmt.Errorf("invalid wallet address %s: %w", walletAddress, err) } } else { if storageWalletPath == "" { return fmt.Errorf("missing wallet path (use '--%s ')", morphUtil.StorageWalletFlag) } var w *wallet.Wallet if createWallet { w, err = wallet.NewWallet(storageWalletPath) } else { w, err = wallet.NewWalletFromFile(storageWalletPath) } if err != nil { return fmt.Errorf("can't create wallet: %w", err) } if createWallet { var password string label, _ := cmd.Flags().GetString(storageWalletLabelFlag) password, err := config.GetStoragePassword(viper.GetViper(), label) if err != nil { return fmt.Errorf("can't fetch password: %w", err) } if label == "" { label = morphUtil.SingleAccountName } if err := w.CreateAccount(label, password); err != nil { return fmt.Errorf("can't create account: %w", err) } } gasReceiver = w.Accounts[0].Contract.ScriptHash() } gasStr := viper.GetString(gasFlag) gasAmount, err := morphUtil.ParseGASAmount(gasStr) if err != nil { return err } wCtx, err := morphUtil.NewInitializeContext(cmd, viper.GetViper()) if err != nil { return err } bw := io.NewBufBinWriter() emit.AppCall(bw.BinWriter, gas.Hash, "transfer", callflag.All, wCtx.CommitteeAcc.Contract.ScriptHash(), gasReceiver, int64(gasAmount), nil) emit.Opcodes(bw.BinWriter, opcode.ASSERT) if bw.Err != nil { return fmt.Errorf("BUG: invalid transfer arguments: %w", bw.Err) } if err := wCtx.SendCommitteeTx(bw.Bytes(), false); err != nil { return err } return wCtx.AwaitTx() }