2021-07-09 09:53:10 +00:00
|
|
|
package morph
|
|
|
|
|
|
|
|
import (
|
2021-07-17 12:51:43 +00:00
|
|
|
"errors"
|
2021-07-17 12:09:23 +00:00
|
|
|
"fmt"
|
2022-02-07 15:28:22 +00:00
|
|
|
"os"
|
2022-02-02 13:28:08 +00:00
|
|
|
"path/filepath"
|
2021-07-17 12:09:23 +00:00
|
|
|
|
2023-03-07 13:38:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
|
2021-07-17 12:09:23 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2022-09-28 08:19:57 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
2021-07-26 08:01:53 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
2022-11-23 14:44:03 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
2021-07-17 12:09:23 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
2021-07-26 08:01:53 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
2022-09-28 08:19:57 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2021-07-26 08:01:53 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
2022-04-05 14:05:37 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
2021-07-17 12:09:23 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
2021-07-09 09:53:10 +00:00
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/viper"
|
2023-05-05 13:44:02 +00:00
|
|
|
"golang.org/x/sync/errgroup"
|
2021-07-09 09:53:10 +00:00
|
|
|
)
|
|
|
|
|
2021-07-17 13:00:22 +00:00
|
|
|
const (
|
|
|
|
singleAccountName = "single"
|
|
|
|
committeeAccountName = "committee"
|
|
|
|
consensusAccountName = "consensus"
|
|
|
|
)
|
|
|
|
|
2023-04-26 08:24:40 +00:00
|
|
|
func generateAlphabetCreds(cmd *cobra.Command, _ []string) error {
|
2021-07-09 09:53:10 +00:00
|
|
|
// alphabet size is not part of the config
|
|
|
|
size, err := cmd.Flags().GetUint(alphabetSizeFlag)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-17 12:51:43 +00:00
|
|
|
if size == 0 {
|
|
|
|
return errors.New("size must be > 0")
|
|
|
|
}
|
2023-05-16 08:22:20 +00:00
|
|
|
if size > maxAlphabetNodes {
|
|
|
|
return ErrTooManyAlphabetNodes
|
|
|
|
}
|
2021-07-09 09:53:10 +00:00
|
|
|
|
2022-04-08 14:58:21 +00:00
|
|
|
v := viper.GetViper()
|
2021-08-04 17:47:58 +00:00
|
|
|
walletDir := config.ResolveHomePath(viper.GetString(alphabetWalletsFlag))
|
2022-04-08 14:58:21 +00:00
|
|
|
pwds, err := initializeWallets(v, walletDir, int(size))
|
2021-07-17 12:17:23 +00:00
|
|
|
if err != nil {
|
2021-07-17 12:09:23 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-07-18 08:36:36 +00:00
|
|
|
_, err = initializeContractWallet(v, walletDir)
|
2021-11-29 12:48:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-07-09 09:53:10 +00:00
|
|
|
cmd.Println("size:", size)
|
2021-07-17 12:09:23 +00:00
|
|
|
cmd.Println("alphabet-wallets:", walletDir)
|
2021-07-09 09:53:10 +00:00
|
|
|
for i := range pwds {
|
|
|
|
cmd.Printf("wallet[%d]: %s\n", i, pwds[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-04-08 14:58:21 +00:00
|
|
|
func initializeWallets(v *viper.Viper, walletDir string, size int) ([]string, error) {
|
2021-07-17 12:09:23 +00:00
|
|
|
wallets := make([]*wallet.Wallet, size)
|
|
|
|
pubs := make(keys.PublicKeys, size)
|
2021-07-17 12:17:23 +00:00
|
|
|
passwords := make([]string, size)
|
2021-07-17 12:09:23 +00:00
|
|
|
|
|
|
|
for i := range wallets {
|
2022-04-08 14:58:21 +00:00
|
|
|
password, err := config.GetPassword(v, innerring.GlagoliticLetter(i).String())
|
2021-07-17 12:17:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("can't fetch password: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-02-02 13:28:08 +00:00
|
|
|
p := filepath.Join(walletDir, innerring.GlagoliticLetter(i).String()+".json")
|
2022-02-07 15:28:22 +00:00
|
|
|
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)
|
|
|
|
}
|
2021-07-17 12:09:23 +00:00
|
|
|
w, err := wallet.NewWallet(p)
|
|
|
|
if err != nil {
|
2021-07-17 12:17:23 +00:00
|
|
|
return nil, fmt.Errorf("can't create wallet: %w", err)
|
2021-07-17 12:09:23 +00:00
|
|
|
}
|
2021-07-17 13:00:22 +00:00
|
|
|
if err := w.CreateAccount(singleAccountName, password); err != nil {
|
2021-07-17 12:17:23 +00:00
|
|
|
return nil, fmt.Errorf("can't create account: %w", err)
|
2021-07-17 12:09:23 +00:00
|
|
|
}
|
|
|
|
|
2021-07-17 12:17:23 +00:00
|
|
|
passwords[i] = password
|
2021-07-17 12:09:23 +00:00
|
|
|
wallets[i] = w
|
|
|
|
pubs[i] = w.Accounts[0].PrivateKey().PublicKey()
|
|
|
|
}
|
|
|
|
|
2023-05-05 13:44:02 +00:00
|
|
|
var errG errgroup.Group
|
|
|
|
|
2021-07-17 12:09:23 +00:00
|
|
|
// 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)
|
2023-05-05 13:44:02 +00:00
|
|
|
for i := range wallets {
|
|
|
|
i := i
|
2023-05-15 08:50:20 +00:00
|
|
|
ps := make(keys.PublicKeys, len(pubs))
|
|
|
|
copy(ps, pubs)
|
2023-05-05 13:44:02 +00:00
|
|
|
errG.Go(func() error {
|
2023-05-15 08:50:20 +00:00
|
|
|
if err := addMultisigAccount(wallets[i], majCount, committeeAccountName, passwords[i], ps); err != nil {
|
2023-05-05 13:44:02 +00:00
|
|
|
return fmt.Errorf("can't create committee account: %w", err)
|
|
|
|
}
|
2023-05-15 08:50:20 +00:00
|
|
|
if err := addMultisigAccount(wallets[i], bftCount, consensusAccountName, passwords[i], ps); err != nil {
|
2023-05-05 13:44:02 +00:00
|
|
|
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
|
|
|
|
})
|
2021-07-17 12:09:23 +00:00
|
|
|
}
|
2023-05-05 13:44:02 +00:00
|
|
|
if err := errG.Wait(); err != nil {
|
|
|
|
return nil, err
|
2021-07-17 12:09:23 +00:00
|
|
|
}
|
2021-07-17 12:17:23 +00:00
|
|
|
return passwords, nil
|
2021-07-17 12:09:23 +00:00
|
|
|
}
|
|
|
|
|
2021-07-17 13:00:22 +00:00
|
|
|
func addMultisigAccount(w *wallet.Wallet, m int, name, password string, pubs keys.PublicKeys) error {
|
2021-07-17 12:09:23 +00:00
|
|
|
acc := wallet.NewAccountFromPrivateKey(w.Accounts[0].PrivateKey())
|
2021-07-17 13:00:22 +00:00
|
|
|
acc.Label = name
|
|
|
|
|
2021-07-17 12:09:23 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-08-25 07:35:16 +00:00
|
|
|
func generateStorageCreds(cmd *cobra.Command, _ []string) error {
|
|
|
|
return refillGas(cmd, storageGasConfigFlag, true)
|
|
|
|
}
|
|
|
|
|
2022-09-28 08:19:57 +00:00
|
|
|
func refillGas(cmd *cobra.Command, gasFlag string, createWallet bool) (err error) {
|
2021-07-09 09:53:10 +00:00
|
|
|
// storage wallet path is not part of the config
|
2022-09-28 08:19:57 +00:00
|
|
|
storageWalletPath, _ := cmd.Flags().GetString(storageWalletFlag)
|
|
|
|
// wallet address is not part of the config
|
|
|
|
walletAddress, _ := cmd.Flags().GetString(walletAddressFlag)
|
2021-07-09 09:53:10 +00:00
|
|
|
|
2022-09-28 08:19:57 +00:00
|
|
|
var gasReceiver util.Uint160
|
2021-08-25 07:35:16 +00:00
|
|
|
|
2022-09-28 08:19:57 +00:00
|
|
|
if len(walletAddress) != 0 {
|
|
|
|
gasReceiver, err = address.StringToUint160(walletAddress)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid wallet address %s: %w", walletAddress, err)
|
|
|
|
}
|
2021-08-25 07:35:16 +00:00
|
|
|
} else {
|
2022-09-28 08:19:57 +00:00
|
|
|
if storageWalletPath == "" {
|
|
|
|
return fmt.Errorf("missing wallet path (use '--%s <out.json>')", storageWalletFlag)
|
|
|
|
}
|
2021-07-09 09:53:10 +00:00
|
|
|
|
2022-09-28 08:19:57 +00:00
|
|
|
var w *wallet.Wallet
|
2021-07-26 08:01:53 +00:00
|
|
|
|
2022-09-28 08:19:57 +00:00
|
|
|
if createWallet {
|
|
|
|
w, err = wallet.NewWallet(storageWalletPath)
|
|
|
|
} else {
|
|
|
|
w, err = wallet.NewWalletFromFile(storageWalletPath)
|
|
|
|
}
|
2022-06-23 07:45:56 +00:00
|
|
|
|
2021-08-25 07:35:16 +00:00
|
|
|
if err != nil {
|
2022-09-28 08:19:57 +00:00
|
|
|
return fmt.Errorf("can't create wallet: %w", err)
|
2021-08-25 07:35:16 +00:00
|
|
|
}
|
2021-07-26 08:01:53 +00:00
|
|
|
|
2022-09-28 08:19:57 +00:00
|
|
|
if createWallet {
|
|
|
|
var password string
|
2022-06-23 07:45:56 +00:00
|
|
|
|
2022-09-28 08:19:57 +00:00
|
|
|
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 = singleAccountName
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := w.CreateAccount(label, password); err != nil {
|
|
|
|
return fmt.Errorf("can't create account: %w", err)
|
|
|
|
}
|
2021-08-25 07:35:16 +00:00
|
|
|
}
|
2022-09-28 08:19:57 +00:00
|
|
|
|
|
|
|
gasReceiver = w.Accounts[0].Contract.ScriptHash()
|
2021-07-26 08:01:53 +00:00
|
|
|
}
|
|
|
|
|
2021-08-25 07:35:16 +00:00
|
|
|
gasStr := viper.GetString(gasFlag)
|
|
|
|
|
2021-11-30 11:57:58 +00:00
|
|
|
gasAmount, err := parseGASAmount(gasStr)
|
2021-07-26 08:01:53 +00:00
|
|
|
if err != nil {
|
2021-11-30 11:57:58 +00:00
|
|
|
return err
|
2021-07-26 08:01:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wCtx, err := newInitializeContext(cmd, viper.GetViper())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bw := io.NewBufBinWriter()
|
2022-11-23 14:44:03 +00:00
|
|
|
emit.AppCall(bw.BinWriter, gas.Hash, "transfer", callflag.All,
|
2022-09-28 08:19:57 +00:00
|
|
|
wCtx.CommitteeAcc.Contract.ScriptHash(), gasReceiver, int64(gasAmount), nil)
|
2022-04-05 14:05:37 +00:00
|
|
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
2021-07-26 08:01:53 +00:00
|
|
|
if bw.Err != nil {
|
|
|
|
return fmt.Errorf("BUG: invalid transfer arguments: %w", bw.Err)
|
|
|
|
}
|
|
|
|
|
2022-08-29 19:31:32 +00:00
|
|
|
if err := wCtx.sendCommitteeTx(bw.Bytes(), false); err != nil {
|
2021-07-26 08:01:53 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return wCtx.awaitTx()
|
2021-07-09 09:53:10 +00:00
|
|
|
}
|
2021-11-30 11:57:58 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|