package helper import ( "archive/tar" "compress/gzip" "errors" "fmt" "io" "os" "path/filepath" "strconv" "strings" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "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/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/management" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/spf13/viper" ) func GetAlphabetWallets(v *viper.Viper, walletDir string) ([]*wallet.Wallet, error) { wallets, err := openAlphabetWallets(v, walletDir) if err != nil { return nil, err } if len(wallets) > constants.MaxAlphabetNodes { return nil, ErrTooManyAlphabetNodes } return wallets, nil } func openAlphabetWallets(v *viper.Viper, walletDir string) ([]*wallet.Wallet, error) { walletFiles, err := os.ReadDir(walletDir) if err != nil { return nil, fmt.Errorf("can't read alphabet wallets dir: %w", err) } var wallets []*wallet.Wallet var letter string for i := range constants.MaxAlphabetNodes { letter = innerring.GlagoliticLetter(i).String() p := filepath.Join(walletDir, letter+".json") var w *wallet.Wallet w, err = wallet.NewWalletFromFile(p) if err != nil { if errors.Is(err, os.ErrNotExist) { err = nil } else { err = fmt.Errorf("can't open wallet: %w", err) } break } var password string password, err = config.GetPassword(v, letter) if err != nil { err = fmt.Errorf("can't fetch password: %w", err) break } for i := range w.Accounts { if err = w.Accounts[i].Decrypt(password, keys.NEP2ScryptParams()); err != nil { err = fmt.Errorf("can't unlock wallet: %w", err) break } } wallets = append(wallets, w) } if err != nil { return nil, fmt.Errorf("can't read wallet for letter '%s': %w", letter, err) } if len(wallets) == 0 { err = errors.New("there are no alphabet wallets in dir (run `generate-alphabet` command first)") if len(walletFiles) > 0 { err = fmt.Errorf("use glagolitic names for wallets(run `print-alphabet`): %w", err) } return nil, err } return wallets, nil } func NewActor(c actor.RPCActor, committeeAcc *wallet.Account) (*actor.Actor, error) { return actor.New(c, []actor.SignerAccount{{ Signer: transaction.Signer{ Account: committeeAcc.Contract.ScriptHash(), Scopes: transaction.Global, }, Account: committeeAcc, }}) } func ReadContract(ctrPath, ctrName string) (*ContractState, error) { rawNef, err := os.ReadFile(filepath.Join(ctrPath, ctrName+"_contract.nef")) if err != nil { return nil, fmt.Errorf("can't read NEF file for %s contract: %w", ctrName, err) } rawManif, err := os.ReadFile(filepath.Join(ctrPath, "config.json")) if err != nil { return nil, fmt.Errorf("can't read manifest file for %s contract: %w", ctrName, err) } cs := &ContractState{ RawNEF: rawNef, RawManifest: rawManif, } return cs, cs.Parse() } func readContractsFromArchive(file io.Reader, names []string) (map[string]*ContractState, error) { m := make(map[string]*ContractState, len(names)) for i := range names { m[names[i]] = new(ContractState) } gr, err := gzip.NewReader(file) if err != nil { return nil, fmt.Errorf("contracts file must be tar.gz archive: %w", err) } r := tar.NewReader(gr) var h *tar.Header for h, err = r.Next(); err == nil && h != nil; h, err = r.Next() { if h.Typeflag != tar.TypeReg { continue } dir, _ := filepath.Split(h.Name) ctrName := filepath.Base(dir) cs, ok := m[ctrName] if !ok { continue } switch { case strings.HasSuffix(h.Name, filepath.Join(ctrName, ctrName+"_contract.nef")): cs.RawNEF, err = io.ReadAll(r) if err != nil { return nil, fmt.Errorf("can't read NEF file for %s contract: %w", ctrName, err) } case strings.HasSuffix(h.Name, "config.json"): cs.RawManifest, err = io.ReadAll(r) if err != nil { return nil, fmt.Errorf("can't read manifest file for %s contract: %w", ctrName, err) } } m[ctrName] = cs } if err != nil && err != io.EOF { return nil, fmt.Errorf("can't read contracts from archive: %w", err) } for ctrName, cs := range m { if cs.RawNEF == nil { return nil, fmt.Errorf("NEF for %s contract wasn't found", ctrName) } if cs.RawManifest == nil { return nil, fmt.Errorf("manifest for %s contract wasn't found", ctrName) } } return m, nil } func GetAlphabetNNSDomain(i int) string { return constants.AlphabetContract + strconv.FormatUint(uint64(i), 10) + ".frostfs" } 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 } // GetContractByID retrieves a contract by its ID using the standard GetContractByID method. // However, if the returned state.Contract is nil, it returns an error indicating that the contract was not found. // See https://git.frostfs.info/TrueCloudLab/frostfs-node/issues/1210 func GetContractByID(r *management.ContractReader, id int32) (*state.Contract, error) { cs, err := r.GetContractByID(id) if err != nil { return nil, err } if cs == nil { return nil, errors.New("contract not found") } return cs, nil }