forked from TrueCloudLab/frostfs-node
Ekaterina Lebedeva
a685fcdc96
Since Go 1.22 a "for" statement with a "range" clause is able to iterate through integer values from zero to an upper limit. gopatch script: @@ var i, e expression @@ -for i := 0; i <= e - 1; i++ { +for i := range e { ... } @@ var i, e expression @@ -for i := 0; i <= e; i++ { +for i := range e + 1 { ... } @@ var i, e expression @@ -for i := 0; i < e; i++ { +for i := range e { ... } Signed-off-by: Ekaterina Lebedeva <ekaterina.lebedeva@yadro.com>
200 lines
5.5 KiB
Go
200 lines
5.5 KiB
Go
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
|
|
}
|