package morph import ( "errors" "fmt" "os" "path/filepath" "github.com/nspcc-dev/neo-go/cli/input" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "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/nspcc-dev/neofs-node/cmd/neofs-adm/internal/modules/config" "github.com/nspcc-dev/neofs-node/pkg/innerring" "github.com/spf13/cobra" "github.com/spf13/viper" ) const ( singleAccountName = "single" committeeAccountName = "committee" consensusAccountName = "consensus" ) func generateAlphabetCreds(cmd *cobra.Command, args []string) error { // alphabet size is not part of the config size, err := cmd.Flags().GetUint(alphabetSizeFlag) if err != nil { return err } if size == 0 { return errors.New("size must be > 0") } walletDir := config.ResolveHomePath(viper.GetString(alphabetWalletsFlag)) pwds, err := initializeWallets(walletDir, int(size)) if err != nil { return err } w, err := initializeContractWallet(walletDir) if err != nil { return err } w.Close() 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(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.AlphabetPassword(viper.GetViper(), i) 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, 0644) 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(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() } // Create committee account with N/2+1 multi-signature. majCount := smartcontract.GetMajorityHonestNodeCount(size) for i, w := range wallets { if err := addMultisigAccount(w, majCount, committeeAccountName, passwords[i], pubs); err != nil { return nil, fmt.Errorf("can't create committee account: %w", err) } } // Create consensus account with 2*N/3+1 multi-signature. bftCount := smartcontract.GetDefaultHonestNodeCount(size) for i, w := range wallets { if err := addMultisigAccount(w, bftCount, consensusAccountName, passwords[i], pubs); err != nil { return nil, fmt.Errorf("can't create consensus account: %w", err) } } for _, w := range wallets { if err := w.Save(); err != nil { return nil, fmt.Errorf("can't save wallet: %w", err) } w.Close() } 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) error { // storage wallet path is not part of the config storageWalletPath, err := cmd.Flags().GetString(storageWalletFlag) if err != nil { return err } if storageWalletPath == "" { return fmt.Errorf("missing wallet path (use '--%s ')", 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 { password, err := input.ReadPassword("New password > ") if err != nil { return fmt.Errorf("can't fetch password: %w", err) } if err := w.CreateAccount(singleAccountName, password); err != nil { return fmt.Errorf("can't create account: %w", err) } } gasStr := viper.GetString(gasFlag) gasAmount, err := parseGASAmount(gasStr) if err != nil { return err } wCtx, err := newInitializeContext(cmd, viper.GetViper()) if err != nil { return err } gasHash := wCtx.nativeHash(nativenames.Gas) bw := io.NewBufBinWriter() emit.AppCall(bw.BinWriter, gasHash, "transfer", callflag.All, wCtx.CommitteeAcc.Contract.ScriptHash(), w.Accounts[0].Contract.ScriptHash(), 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(), -1, false); err != nil { return err } return wCtx.awaitTx() } func parseGASAmount(s string) (fixedn.Fixed8, error) { gasAmount, err := fixedn.Fixed8FromString(s) if err != nil { return 0, fmt.Errorf("invalid GAS amount %s: %w", s, err) } if gasAmount <= 0 { return 0, fmt.Errorf("GAS amount must be positive (got %d)", gasAmount) } return gasAmount, nil }