frostfs-adm refactorings #161
8 changed files with 627 additions and 409 deletions
|
@ -152,7 +152,6 @@ func listContainers(cmd *cobra.Command, _ []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: funlen
|
|
||||||
func restoreContainers(cmd *cobra.Command, _ []string) error {
|
func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||||
filename, err := cmd.Flags().GetString(containerDumpFlag)
|
filename, err := cmd.Flags().GetString(containerDumpFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -165,25 +164,14 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
defer wCtx.close()
|
defer wCtx.close()
|
||||||
|
|
||||||
nnsCs, err := wCtx.Client.GetContractStateByID(1)
|
containers, err := parseContainers(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't get NNS contract state: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ch, err := nnsResolveHash(wCtx.ReadOnlyInvoker, nnsCs.Hash, containerContract+".frostfs")
|
ch, err := fetchContainerContractHash(wCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't fetch container contract hash: %w", err)
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
data, err := os.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't read dump file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var containers []Container
|
|
||||||
err = json.Unmarshal(data, &containers)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't parse dump file: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isOK, err := getCIDFilterFunc(cmd)
|
isOK, err := getCIDFilterFunc(cmd)
|
||||||
|
@ -191,6 +179,15 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = restoreOrPutContainers(containers, isOK, cmd, wCtx, ch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return wCtx.awaitTx()
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreOrPutContainers(containers []Container, isOK func([]byte) bool, cmd *cobra.Command, wCtx *initializeContext, ch util.Uint160) error {
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
for _, cnt := range containers {
|
for _, cnt := range containers {
|
||||||
hv := hash.Sha256(cnt.Value)
|
hv := hash.Sha256(cnt.Value)
|
||||||
|
@ -198,33 +195,18 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bw.Reset()
|
bw.Reset()
|
||||||
emit.AppCall(bw.BinWriter, ch, "get", callflag.All, hv.BytesBE())
|
restored, err := isContainerRestored(cmd, wCtx, ch, bw, hv)
|
||||||
res, err := wCtx.Client.InvokeScript(bw.Bytes(), nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("can't check if container is already restored: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
if len(res.Stack) == 0 {
|
if restored {
|
||||||
return errors.New("empty stack")
|
|
||||||
}
|
|
||||||
|
|
||||||
old := new(Container)
|
|
||||||
if err := old.FromStackItem(res.Stack[0]); err != nil {
|
|
||||||
return fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
|
||||||
}
|
|
||||||
if len(old.Value) != 0 {
|
|
||||||
var id cid.ID
|
|
||||||
id.SetSHA256(hv)
|
|
||||||
cmd.Printf("Container %s is already deployed.\n", id)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
bw.Reset()
|
bw.Reset()
|
||||||
emit.AppCall(bw.BinWriter, ch, "put", callflag.All,
|
|
||||||
cnt.Value, cnt.Signature, cnt.PublicKey, cnt.Token)
|
putContainer(bw, ch, cnt)
|
||||||
if ea := cnt.EACL; ea != nil {
|
|
||||||
emit.AppCall(bw.BinWriter, ch, "setEACL", callflag.All,
|
|
||||||
ea.Value, ea.Signature, ea.PublicKey, ea.Token)
|
|
||||||
}
|
|
||||||
if bw.Err != nil {
|
if bw.Err != nil {
|
||||||
panic(bw.Err)
|
panic(bw.Err)
|
||||||
}
|
}
|
||||||
|
@ -233,8 +215,67 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return wCtx.awaitTx()
|
func putContainer(bw *io.BufBinWriter, ch util.Uint160, cnt Container) {
|
||||||
|
emit.AppCall(bw.BinWriter, ch, "put", callflag.All,
|
||||||
|
cnt.Value, cnt.Signature, cnt.PublicKey, cnt.Token)
|
||||||
|
if ea := cnt.EACL; ea != nil {
|
||||||
|
emit.AppCall(bw.BinWriter, ch, "setEACL", callflag.All,
|
||||||
|
ea.Value, ea.Signature, ea.PublicKey, ea.Token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isContainerRestored(cmd *cobra.Command, wCtx *initializeContext, containerHash util.Uint160, bw *io.BufBinWriter, hashValue util.Uint256) (bool, error) {
|
||||||
|
emit.AppCall(bw.BinWriter, containerHash, "get", callflag.All, hashValue.BytesBE())
|
||||||
|
res, err := wCtx.Client.InvokeScript(bw.Bytes(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("can't check if container is already restored: %w", err)
|
||||||
|
}
|
||||||
|
if len(res.Stack) == 0 {
|
||||||
|
return false, errors.New("empty stack")
|
||||||
|
}
|
||||||
|
|
||||||
|
old := new(Container)
|
||||||
|
if err := old.FromStackItem(res.Stack[0]); err != nil {
|
||||||
|
return false, fmt.Errorf("%w: %v", errInvalidContainerResponse, err)
|
||||||
|
}
|
||||||
|
if len(old.Value) != 0 {
|
||||||
|
var id cid.ID
|
||||||
|
id.SetSHA256(hashValue)
|
||||||
|
cmd.Printf("Container %s is already deployed.\n", id)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseContainers(filename string) ([]Container, error) {
|
||||||
|
data, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't read dump file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var containers []Container
|
||||||
|
err = json.Unmarshal(data, &containers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't parse dump file: %w", err)
|
||||||
|
}
|
||||||
|
return containers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchContainerContractHash(wCtx *initializeContext) (util.Uint160, error) {
|
||||||
|
nnsCs, err := wCtx.Client.GetContractStateByID(1)
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, err := nnsResolveHash(wCtx.ReadOnlyInvoker, nnsCs.Hash, containerContract+".frostfs")
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint160{}, fmt.Errorf("can't fetch container contract hash: %w", err)
|
||||||
|
}
|
||||||
|
return ch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container represents container struct in contract storage.
|
// Container represents container struct in contract storage.
|
||||||
|
|
|
@ -57,7 +57,6 @@ func init() {
|
||||||
ff.String(customZoneFlag, "frostfs", "Custom zone for NNS")
|
ff.String(customZoneFlag, "frostfs", "Custom zone for NNS")
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: funlen
|
|
||||||
func deployContractCmd(cmd *cobra.Command, args []string) error {
|
func deployContractCmd(cmd *cobra.Command, args []string) error {
|
||||||
v := viper.GetViper()
|
v := viper.GetViper()
|
||||||
c, err := newInitializeContext(cmd, v)
|
c, err := newInitializeContext(cmd, v)
|
||||||
|
@ -101,80 +100,88 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
|
||||||
cs.Manifest.Name)
|
cs.Manifest.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
writer := io.NewBufBinWriter()
|
||||||
if err := emitDeploymentArguments(w.BinWriter, args); err != nil {
|
if err := emitDeploymentArguments(writer.BinWriter, args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
emit.Bytes(w.BinWriter, cs.RawManifest)
|
emit.Bytes(writer.BinWriter, cs.RawManifest)
|
||||||
emit.Bytes(w.BinWriter, cs.RawNEF)
|
emit.Bytes(writer.BinWriter, cs.RawNEF)
|
||||||
emit.Int(w.BinWriter, 3)
|
emit.Int(writer.BinWriter, 3)
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
emit.Opcodes(writer.BinWriter, opcode.PACK)
|
||||||
emit.AppCallNoArgs(w.BinWriter, callHash, method, callflag.All)
|
emit.AppCallNoArgs(writer.BinWriter, callHash, method, callflag.All)
|
||||||
emit.Opcodes(w.BinWriter, opcode.DROP) // contract state on stack
|
emit.Opcodes(writer.BinWriter, opcode.DROP) // contract state on stack
|
||||||
if !isUpdate {
|
if !isUpdate {
|
||||||
bw := io.NewBufBinWriter()
|
err := registerNNS(nnsCs, c, zone, domain, cs, writer)
|
||||||
emit.Instruction(bw.BinWriter, opcode.INITSSLOT, []byte{1})
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "getPrice", callflag.All)
|
|
||||||
emit.Opcodes(bw.BinWriter, opcode.STSFLD0)
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "setPrice", callflag.All, 1)
|
|
||||||
|
|
||||||
start := bw.Len()
|
|
||||||
needRecord := false
|
|
||||||
|
|
||||||
ok, err := c.nnsRootRegistered(nnsCs.Hash, zone)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !ok {
|
|
||||||
needRecord = true
|
|
||||||
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
|
||||||
zone, c.CommitteeAcc.Contract.ScriptHash(),
|
|
||||||
"ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
|
||||||
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
|
||||||
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
|
||||||
"ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
|
||||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
|
||||||
} else {
|
|
||||||
s, ok, err := c.nnsRegisterDomainScript(nnsCs.Hash, cs.Hash, domain)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
needRecord = !ok
|
|
||||||
if len(s) != 0 {
|
|
||||||
bw.WriteBytes(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if needRecord {
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "deleteRecords", callflag.All, domain, int64(nns.TXT))
|
|
||||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "addRecord", callflag.All,
|
|
||||||
domain, int64(nns.TXT), address.Uint160ToString(cs.Hash))
|
|
||||||
}
|
|
||||||
|
|
||||||
if bw.Err != nil {
|
|
||||||
panic(fmt.Errorf("BUG: can't create deployment script: %w", w.Err))
|
|
||||||
} else if bw.Len() != start {
|
|
||||||
w.WriteBytes(bw.Bytes())
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.PUSH1, opcode.PACK)
|
|
||||||
emit.AppCallNoArgs(w.BinWriter, nnsCs.Hash, "setPrice", callflag.All)
|
|
||||||
|
|
||||||
if needRecord {
|
|
||||||
c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.Err != nil {
|
if writer.Err != nil {
|
||||||
panic(fmt.Errorf("BUG: can't create deployment script: %w", w.Err))
|
panic(fmt.Errorf("BUG: can't create deployment script: %w", writer.Err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
if err := c.sendCommitteeTx(writer.Bytes(), false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return c.awaitTx()
|
return c.awaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func registerNNS(nnsCs *state.Contract, c *initializeContext, zone string, domain string, cs *contractState, writer *io.BufBinWriter) error {
|
||||||
|
bw := io.NewBufBinWriter()
|
||||||
|
emit.Instruction(bw.BinWriter, opcode.INITSSLOT, []byte{1})
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "getPrice", callflag.All)
|
||||||
|
emit.Opcodes(bw.BinWriter, opcode.STSFLD0)
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "setPrice", callflag.All, 1)
|
||||||
|
|
||||||
|
start := bw.Len()
|
||||||
|
needRecord := false
|
||||||
|
|
||||||
|
ok, err := c.nnsRootRegistered(nnsCs.Hash, zone)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if !ok {
|
||||||
|
needRecord = true
|
||||||
|
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||||
|
zone, c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
|
"ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
||||||
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||||
|
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
||||||
|
"ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600))
|
||||||
|
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||||
|
} else {
|
||||||
|
s, ok, err := c.nnsRegisterDomainScript(nnsCs.Hash, cs.Hash, domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
needRecord = !ok
|
||||||
|
if len(s) != 0 {
|
||||||
|
bw.WriteBytes(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if needRecord {
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "deleteRecords", callflag.All, domain, int64(nns.TXT))
|
||||||
|
emit.AppCall(bw.BinWriter, nnsCs.Hash, "addRecord", callflag.All,
|
||||||
|
domain, int64(nns.TXT), address.Uint160ToString(cs.Hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
if bw.Err != nil {
|
||||||
|
panic(fmt.Errorf("BUG: can't create deployment script: %w", writer.Err))
|
||||||
|
} else if bw.Len() != start {
|
||||||
|
writer.WriteBytes(bw.Bytes())
|
||||||
|
emit.Opcodes(writer.BinWriter, opcode.LDSFLD0, opcode.PUSH1, opcode.PACK)
|
||||||
|
emit.AppCallNoArgs(writer.BinWriter, nnsCs.Hash, "setPrice", callflag.All)
|
||||||
|
|
||||||
|
if needRecord {
|
||||||
|
c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func emitDeploymentArguments(w *io.BinWriter, args []string) error {
|
func emitDeploymentArguments(w *io.BinWriter, args []string) error {
|
||||||
_, ps, err := cmdargs.ParseParams(args, true)
|
_, ps, err := cmdargs.ParseParams(args, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -108,7 +108,6 @@ func (c *initializeContext) close() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: funlen
|
|
||||||
func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContext, error) {
|
func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContext, error) {
|
||||||
walletDir := config.ResolveHomePath(viper.GetString(alphabetWalletsFlag))
|
walletDir := config.ResolveHomePath(viper.GetString(alphabetWalletsFlag))
|
||||||
wallets, err := openAlphabetWallets(v, walletDir)
|
wallets, err := openAlphabetWallets(v, walletDir)
|
||||||
|
@ -119,24 +118,14 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
|
||||||
needContracts := cmd.Name() == "update-contracts" || cmd.Name() == "init"
|
needContracts := cmd.Name() == "update-contracts" || cmd.Name() == "init"
|
||||||
|
|
||||||
var w *wallet.Wallet
|
var w *wallet.Wallet
|
||||||
if needContracts {
|
w, err = getWallet(cmd, v, needContracts, walletDir)
|
||||||
w, err = openContractWallet(v, cmd, walletDir)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var c Client
|
c, err := createClient(cmd, v, wallets)
|
||||||
if v.GetString(localDumpFlag) != "" {
|
|
||||||
if v.GetString(endpointFlag) != "" {
|
|
||||||
return nil, fmt.Errorf("`%s` and `%s` flags are mutually exclusive", endpointFlag, localDumpFlag)
|
|
||||||
}
|
|
||||||
c, err = newLocalClient(cmd, v, wallets)
|
|
||||||
} else {
|
|
||||||
c, err = getN3Client(v)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
committeeAcc, err := getWalletAccount(wallets[0], committeeAccountName)
|
committeeAcc, err := getWalletAccount(wallets[0], committeeAccountName)
|
||||||
|
@ -149,35 +138,22 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
|
||||||
return nil, fmt.Errorf("can't find consensus account: %w", err)
|
return nil, fmt.Errorf("can't find consensus account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ctrPath string
|
if err := validateInit(cmd); err != nil {
|
||||||
if cmd.Name() == "init" {
|
return nil, err
|
||||||
if viper.GetInt64(epochDurationInitFlag) <= 0 {
|
|
||||||
return nil, fmt.Errorf("epoch duration must be positive")
|
|
||||||
}
|
|
||||||
|
|
||||||
if viper.GetInt64(maxObjectSizeInitFlag) <= 0 {
|
|
||||||
return nil, fmt.Errorf("max object size must be positive")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if needContracts {
|
ctrPath, err := getContractsPath(cmd, v, needContracts)
|
||||||
ctrPath, err = cmd.Flags().GetString(contractsInitFlag)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, fmt.Errorf("invalid contracts path: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkNotaryEnabled(c); err != nil {
|
if err := checkNotaryEnabled(c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
accounts := make([]*wallet.Account, len(wallets))
|
accounts, err := createWalletAccounts(wallets)
|
||||||
for i, w := range wallets {
|
if err != nil {
|
||||||
acc, err := getWalletAccount(w, singleAccountName)
|
return nil, err
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("wallet %s is invalid (no single account): %w", w.Path(), err)
|
|
||||||
}
|
|
||||||
accounts[i] = acc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cliCtx, err := defaultClientContext(c, committeeAcc)
|
cliCtx, err := defaultClientContext(c, committeeAcc)
|
||||||
|
@ -207,6 +183,69 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
|
||||||
return initCtx, nil
|
return initCtx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateInit(cmd *cobra.Command) error {
|
||||||
|
if cmd.Name() != "init" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if viper.GetInt64(epochDurationInitFlag) <= 0 {
|
||||||
|
return fmt.Errorf("epoch duration must be positive")
|
||||||
|
}
|
||||||
|
|
||||||
|
if viper.GetInt64(maxObjectSizeInitFlag) <= 0 {
|
||||||
|
return fmt.Errorf("max object size must be positive")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createClient(cmd *cobra.Command, v *viper.Viper, wallets []*wallet.Wallet) (Client, error) {
|
||||||
|
var c Client
|
||||||
|
var err error
|
||||||
|
if v.GetString(localDumpFlag) != "" {
|
||||||
|
if v.GetString(endpointFlag) != "" {
|
||||||
|
return nil, fmt.Errorf("`%s` and `%s` flags are mutually exclusive", endpointFlag, localDumpFlag)
|
||||||
|
}
|
||||||
|
c, err = newLocalClient(cmd, v, wallets)
|
||||||
|
} else {
|
||||||
|
c, err = getN3Client(v)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWallet(cmd *cobra.Command, v *viper.Viper, needContracts bool, walletDir string) (*wallet.Wallet, error) {
|
||||||
|
if !needContracts {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return openContractWallet(v, cmd, walletDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getContractsPath(cmd *cobra.Command, v *viper.Viper, needContracts bool) (string, error) {
|
||||||
|
if !needContracts {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrPath, err := cmd.Flags().GetString(contractsInitFlag)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid contracts path: %w", err)
|
||||||
|
}
|
||||||
|
return ctrPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createWalletAccounts(wallets []*wallet.Wallet) ([]*wallet.Account, error) {
|
||||||
|
accounts := make([]*wallet.Account, len(wallets))
|
||||||
|
for i, w := range wallets {
|
||||||
|
acc, err := getWalletAccount(w, singleAccountName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("wallet %s is invalid (no single account): %w", w.Path(), err)
|
||||||
|
}
|
||||||
|
accounts[i] = acc
|
||||||
|
}
|
||||||
|
return accounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
func openAlphabetWallets(v *viper.Viper, walletDir string) ([]*wallet.Wallet, error) {
|
func openAlphabetWallets(v *viper.Viper, walletDir string) ([]*wallet.Wallet, error) {
|
||||||
walletFiles, err := os.ReadDir(walletDir)
|
walletFiles, err := os.ReadDir(walletDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -156,7 +156,6 @@ func (c *initializeContext) deployNNS(method string) error {
|
||||||
return c.awaitTx()
|
return c.awaitTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: funlen
|
|
||||||
func (c *initializeContext) updateContracts() error {
|
func (c *initializeContext) updateContracts() error {
|
||||||
alphaCs := c.getContract(alphabetContract)
|
alphaCs := c.getContract(alphabetContract)
|
||||||
|
|
||||||
|
@ -168,8 +167,6 @@ func (c *initializeContext) updateContracts() error {
|
||||||
|
|
||||||
w := io2.NewBufBinWriter()
|
w := io2.NewBufBinWriter()
|
||||||
|
|
||||||
var keysParam []any
|
|
||||||
|
|
||||||
// Update script size for a single-node committee is close to the maximum allowed size of 65535.
|
// Update script size for a single-node committee is close to the maximum allowed size of 65535.
|
||||||
// Because of this we want to reuse alphabet contract NEF and manifest for different updates.
|
// Because of this we want to reuse alphabet contract NEF and manifest for different updates.
|
||||||
// The generated script is as following.
|
// The generated script is as following.
|
||||||
|
@ -183,42 +180,36 @@ func (c *initializeContext) updateContracts() error {
|
||||||
emit.Bytes(w.BinWriter, alphaCs.RawNEF)
|
emit.Bytes(w.BinWriter, alphaCs.RawNEF)
|
||||||
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
||||||
|
|
||||||
baseGroups := alphaCs.Manifest.Groups
|
keysParam, err := c.deployAlphabetAccounts(nnsHash, w, alphaCs)
|
||||||
|
if err != nil {
|
||||||
// alphabet contracts should be deployed by individual nodes to get different hashes.
|
return err
|
||||||
for i, acc := range c.Accounts {
|
|
||||||
ctrHash, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, getAlphabetNNSDomain(i))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't resolve hash for contract update: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes())
|
|
||||||
|
|
||||||
params := c.getAlphabetDeployItems(i, len(c.Wallets))
|
|
||||||
emit.Array(w.BinWriter, params...)
|
|
||||||
|
|
||||||
alphaCs.Manifest.Groups = baseGroups
|
|
||||||
err = c.addManifestGroup(ctrHash, alphaCs)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't sign manifest group: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
emit.Bytes(w.BinWriter, alphaCs.RawManifest)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.LDSFLD0)
|
|
||||||
emit.Int(w.BinWriter, 3)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
||||||
emit.AppCallNoArgs(w.BinWriter, ctrHash, updateMethodName, callflag.All)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
|
||||||
if !strings.Contains(err.Error(), common.ErrAlreadyUpdated) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Command.Println("Alphabet contracts are already updated.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Reset()
|
w.Reset()
|
||||||
|
|
||||||
|
if err = c.deployOrUpdateContracts(w, nnsHash, keysParam); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
groupKey := c.ContractWallet.Accounts[0].PrivateKey().PublicKey()
|
||||||
|
_, _, err = c.emitUpdateNNSGroupScript(w, nnsHash, groupKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Command.Printf("NNS: Set %s -> %s\n", morphClient.NNSGroupKeyName, hex.EncodeToString(groupKey.Bytes()))
|
||||||
|
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.LDSFLD0)
|
||||||
|
emit.Int(w.BinWriter, 1)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||||
|
emit.AppCallNoArgs(w.BinWriter, nnsHash, "setPrice", callflag.All)
|
||||||
|
|
||||||
|
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.awaitTx()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *initializeContext) deployOrUpdateContracts(w *io2.BufBinWriter, nnsHash util.Uint160, keysParam []any) error {
|
||||||
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
emit.Instruction(w.BinWriter, opcode.INITSSLOT, []byte{1})
|
||||||
emit.AppCall(w.BinWriter, nnsHash, "getPrice", callflag.All)
|
emit.AppCall(w.BinWriter, nnsHash, "getPrice", callflag.All)
|
||||||
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
||||||
|
@ -278,23 +269,46 @@ func (c *initializeContext) updateContracts() error {
|
||||||
c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE())
|
c.Command.Printf("NNS: Set %s -> %s\n", domain, cs.Hash.StringLE())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
groupKey := c.ContractWallet.Accounts[0].PrivateKey().PublicKey()
|
func (c *initializeContext) deployAlphabetAccounts(nnsHash util.Uint160, w *io2.BufBinWriter, alphaCs *contractState) ([]any, error) {
|
||||||
_, _, err = c.emitUpdateNNSGroupScript(w, nnsHash, groupKey)
|
var keysParam []any
|
||||||
if err != nil {
|
|
||||||
return err
|
baseGroups := alphaCs.Manifest.Groups
|
||||||
|
|
||||||
|
// alphabet contracts should be deployed by individual nodes to get different hashes.
|
||||||
|
for i, acc := range c.Accounts {
|
||||||
|
ctrHash, err := nnsResolveHash(c.ReadOnlyInvoker, nnsHash, getAlphabetNNSDomain(i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't resolve hash for contract update: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keysParam = append(keysParam, acc.PrivateKey().PublicKey().Bytes())
|
||||||
|
|
||||||
|
params := c.getAlphabetDeployItems(i, len(c.Wallets))
|
||||||
|
emit.Array(w.BinWriter, params...)
|
||||||
|
|
||||||
|
alphaCs.Manifest.Groups = baseGroups
|
||||||
|
err = c.addManifestGroup(ctrHash, alphaCs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't sign manifest group: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
emit.Bytes(w.BinWriter, alphaCs.RawManifest)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.LDSFLD0)
|
||||||
|
emit.Int(w.BinWriter, 3)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||||
|
emit.AppCallNoArgs(w.BinWriter, ctrHash, updateMethodName, callflag.All)
|
||||||
}
|
}
|
||||||
c.Command.Printf("NNS: Set %s -> %s\n", morphClient.NNSGroupKeyName, hex.EncodeToString(groupKey.Bytes()))
|
|
||||||
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.LDSFLD0)
|
|
||||||
emit.Int(w.BinWriter, 1)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
|
||||||
emit.AppCallNoArgs(w.BinWriter, nnsHash, "setPrice", callflag.All)
|
|
||||||
|
|
||||||
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
||||||
return err
|
if !strings.Contains(err.Error(), common.ErrAlreadyUpdated) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.Command.Println("Alphabet contracts are already updated.")
|
||||||
}
|
}
|
||||||
return c.awaitTx()
|
|
||||||
|
return keysParam, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *initializeContext) deployContracts() error {
|
func (c *initializeContext) deployContracts() error {
|
||||||
|
|
|
@ -9,10 +9,12 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"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/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
"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/actor"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
@ -22,18 +24,10 @@ import (
|
||||||
// https://github.com/nspcc-dev/neo-go/blob/master/pkg/core/native/notary.go#L48
|
// https://github.com/nspcc-dev/neo-go/blob/master/pkg/core/native/notary.go#L48
|
||||||
const defaultNotaryDepositLifetime = 5760
|
const defaultNotaryDepositLifetime = 5760
|
||||||
|
|
||||||
// nolint: funlen
|
|
||||||
func depositNotary(cmd *cobra.Command, _ []string) error {
|
func depositNotary(cmd *cobra.Command, _ []string) error {
|
||||||
p, err := cmd.Flags().GetString(storageWalletFlag)
|
w, err := openWallet(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if p == "" {
|
|
||||||
return fmt.Errorf("missing wallet path (use '--%s <out.json>')", storageWalletFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := wallet.NewWalletFromFile(p)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't open wallet: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accHash := w.GetChangeAddress()
|
accHash := w.GetChangeAddress()
|
||||||
|
@ -81,6 +75,10 @@ func depositNotary(cmd *cobra.Command, _ []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return transferGas(cmd, acc, accHash, gasAmount, till)
|
||||||
|
}
|
||||||
|
|
||||||
|
func transferGas(cmd *cobra.Command, acc *wallet.Account, accHash util.Uint160, gasAmount fixedn.Fixed8, till int64) error {
|
||||||
c, err := getN3Client(viper.GetViper())
|
c, err := getN3Client(viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -120,3 +118,18 @@ func depositNotary(cmd *cobra.Command, _ []string) error {
|
||||||
|
|
||||||
return awaitTx(cmd, c, []hashVUBPair{{hash: txHash, vub: vub}})
|
return awaitTx(cmd, c, []hashVUBPair{{hash: txHash, vub: vub}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func openWallet(cmd *cobra.Command) (*wallet.Wallet, error) {
|
||||||
|
p, err := cmd.Flags().GetString(storageWalletFlag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if p == "" {
|
||||||
|
return nil, fmt.Errorf("missing wallet path (use '--%s <out.json>')", storageWalletFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := wallet.NewWalletFromFile(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't open wallet: %v", err)
|
||||||
|
}
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
|
@ -238,12 +238,140 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint: funlen
|
|
||||||
func init() {
|
func init() {
|
||||||
RootCmd.AddCommand(generateAlphabetCmd)
|
initGenerateAlphabetCmd()
|
||||||
generateAlphabetCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
initInitCmd()
|
||||||
generateAlphabetCmd.Flags().Uint(alphabetSizeFlag, 7, "Amount of alphabet wallets to generate")
|
initDeployCmd()
|
||||||
|
initGenerateStorageCmd()
|
||||||
|
initForceNewEpochCmd()
|
||||||
|
initRemoveNodesCmd()
|
||||||
|
initSetPolicyCmd()
|
||||||
|
initDumpContractHashesCmd()
|
||||||
|
initDumpNetworkConfigCmd()
|
||||||
|
initSetConfigCmd()
|
||||||
|
initDumpBalancesCmd()
|
||||||
|
initUpdateContractsCmd()
|
||||||
|
initDumpContainersCmd()
|
||||||
|
initRestoreContainersCmd()
|
||||||
|
initListContainersCmd()
|
||||||
|
initRefillGasCmd()
|
||||||
|
initSubnetCmd()
|
||||||
|
initDepositoryNotaryCmd()
|
||||||
|
initNetmapCandidatesCmd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initNetmapCandidatesCmd() {
|
||||||
|
RootCmd.AddCommand(netmapCandidatesCmd)
|
||||||
|
netmapCandidatesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initDepositoryNotaryCmd() {
|
||||||
|
RootCmd.AddCommand(depositNotaryCmd)
|
||||||
|
depositNotaryCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
depositNotaryCmd.Flags().String(storageWalletFlag, "", "Path to storage node wallet")
|
||||||
|
depositNotaryCmd.Flags().String(walletAccountFlag, "", "Wallet account address")
|
||||||
|
depositNotaryCmd.Flags().String(refillGasAmountFlag, "", "Amount of GAS to deposit")
|
||||||
|
depositNotaryCmd.Flags().String(notaryDepositTillFlag, "", "Notary deposit duration in blocks")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSubnetCmd() {
|
||||||
|
RootCmd.AddCommand(cmdSubnet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRefillGasCmd() {
|
||||||
|
RootCmd.AddCommand(refillGasCmd)
|
||||||
|
refillGasCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
refillGasCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
refillGasCmd.Flags().String(storageWalletFlag, "", "Path to storage node wallet")
|
||||||
|
refillGasCmd.Flags().String(walletAddressFlag, "", "Address of wallet")
|
||||||
|
refillGasCmd.Flags().String(refillGasAmountFlag, "", "Additional amount of GAS to transfer")
|
||||||
|
refillGasCmd.MarkFlagsMutuallyExclusive(walletAddressFlag, storageWalletFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initListContainersCmd() {
|
||||||
|
RootCmd.AddCommand(listContainersCmd)
|
||||||
|
listContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
listContainersCmd.Flags().String(containerContractFlag, "", "Container contract hash (for networks without NNS)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRestoreContainersCmd() {
|
||||||
|
RootCmd.AddCommand(restoreContainersCmd)
|
||||||
|
restoreContainersCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
restoreContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
restoreContainersCmd.Flags().String(containerDumpFlag, "", "File to restore containers from")
|
||||||
|
restoreContainersCmd.Flags().StringSlice(containerIDsFlag, nil, "Containers to restore")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initDumpContainersCmd() {
|
||||||
|
RootCmd.AddCommand(dumpContainersCmd)
|
||||||
|
dumpContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
dumpContainersCmd.Flags().String(containerDumpFlag, "", "File where to save dumped containers")
|
||||||
|
dumpContainersCmd.Flags().String(containerContractFlag, "", "Container contract hash (for networks without NNS)")
|
||||||
|
dumpContainersCmd.Flags().StringSlice(containerIDsFlag, nil, "Containers to dump")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initUpdateContractsCmd() {
|
||||||
|
RootCmd.AddCommand(updateContractsCmd)
|
||||||
|
updateContractsCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
updateContractsCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
updateContractsCmd.Flags().String(contractsInitFlag, "", "Path to archive with compiled FrostFS contracts (default fetched from latest github release)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initDumpBalancesCmd() {
|
||||||
|
RootCmd.AddCommand(dumpBalancesCmd)
|
||||||
|
dumpBalancesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
dumpBalancesCmd.Flags().BoolP(dumpBalancesStorageFlag, "s", false, "Dump balances of storage nodes from the current netmap")
|
||||||
|
dumpBalancesCmd.Flags().BoolP(dumpBalancesAlphabetFlag, "a", false, "Dump balances of alphabet contracts")
|
||||||
|
dumpBalancesCmd.Flags().BoolP(dumpBalancesProxyFlag, "p", false, "Dump balances of the proxy contract")
|
||||||
|
dumpBalancesCmd.Flags().Bool(dumpBalancesUseScriptHashFlag, false, "Use script-hash format for addresses")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSetConfigCmd() {
|
||||||
|
RootCmd.AddCommand(setConfig)
|
||||||
|
setConfig.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
setConfig.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
setConfig.Flags().Bool(forceConfigSet, false, "Force setting not well-known configuration key")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initDumpNetworkConfigCmd() {
|
||||||
|
RootCmd.AddCommand(dumpNetworkConfigCmd)
|
||||||
|
dumpNetworkConfigCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initDumpContractHashesCmd() {
|
||||||
|
RootCmd.AddCommand(dumpContractHashesCmd)
|
||||||
|
dumpContractHashesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
dumpContractHashesCmd.Flags().String(customZoneFlag, "", "Custom zone to search.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSetPolicyCmd() {
|
||||||
|
RootCmd.AddCommand(setPolicy)
|
||||||
|
setPolicy.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
setPolicy.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRemoveNodesCmd() {
|
||||||
|
RootCmd.AddCommand(removeNodes)
|
||||||
|
removeNodes.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
removeNodes.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initForceNewEpochCmd() {
|
||||||
|
RootCmd.AddCommand(forceNewEpoch)
|
||||||
|
forceNewEpoch.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
forceNewEpoch.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initGenerateStorageCmd() {
|
||||||
|
RootCmd.AddCommand(generateStorageCmd)
|
||||||
|
generateStorageCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
|
generateStorageCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
generateStorageCmd.Flags().String(storageWalletFlag, "", "Path to new storage node wallet")
|
||||||
|
generateStorageCmd.Flags().String(storageGasCLIFlag, "", "Initial amount of GAS to transfer")
|
||||||
|
generateStorageCmd.Flags().StringP(storageWalletLabelFlag, "l", "", "Wallet label")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initInitCmd() {
|
||||||
RootCmd.AddCommand(initCmd)
|
RootCmd.AddCommand(initCmd)
|
||||||
initCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
initCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
initCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
initCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
@ -256,85 +384,14 @@ func init() {
|
||||||
initCmd.Flags().Uint64(containerAliasFeeCLIFlag, 500, "Container alias fee")
|
initCmd.Flags().Uint64(containerAliasFeeCLIFlag, 500, "Container alias fee")
|
||||||
initCmd.Flags().String(protoConfigPath, "", "Path to the consensus node configuration")
|
initCmd.Flags().String(protoConfigPath, "", "Path to the consensus node configuration")
|
||||||
initCmd.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
|
initCmd.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
|
||||||
|
}
|
||||||
RootCmd.AddCommand(deployCmd)
|
|
||||||
|
func initGenerateAlphabetCmd() {
|
||||||
RootCmd.AddCommand(generateStorageCmd)
|
RootCmd.AddCommand(generateAlphabetCmd)
|
||||||
generateStorageCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
generateAlphabetCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||||
generateStorageCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
generateAlphabetCmd.Flags().Uint(alphabetSizeFlag, 7, "Amount of alphabet wallets to generate")
|
||||||
generateStorageCmd.Flags().String(storageWalletFlag, "", "Path to new storage node wallet")
|
}
|
||||||
generateStorageCmd.Flags().String(storageGasCLIFlag, "", "Initial amount of GAS to transfer")
|
|
||||||
generateStorageCmd.Flags().StringP(storageWalletLabelFlag, "l", "", "Wallet label")
|
func initDeployCmd() {
|
||||||
|
RootCmd.AddCommand(deployCmd)
|
||||||
RootCmd.AddCommand(forceNewEpoch)
|
|
||||||
forceNewEpoch.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
forceNewEpoch.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(removeNodes)
|
|
||||||
removeNodes.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
removeNodes.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(setPolicy)
|
|
||||||
setPolicy.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
setPolicy.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(dumpContractHashesCmd)
|
|
||||||
dumpContractHashesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
dumpContractHashesCmd.Flags().String(customZoneFlag, "", "Custom zone to search.")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(dumpNetworkConfigCmd)
|
|
||||||
dumpNetworkConfigCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(setConfig)
|
|
||||||
setConfig.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
setConfig.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
setConfig.Flags().Bool(forceConfigSet, false, "Force setting not well-known configuration key")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(dumpBalancesCmd)
|
|
||||||
dumpBalancesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
dumpBalancesCmd.Flags().BoolP(dumpBalancesStorageFlag, "s", false, "Dump balances of storage nodes from the current netmap")
|
|
||||||
dumpBalancesCmd.Flags().BoolP(dumpBalancesAlphabetFlag, "a", false, "Dump balances of alphabet contracts")
|
|
||||||
dumpBalancesCmd.Flags().BoolP(dumpBalancesProxyFlag, "p", false, "Dump balances of the proxy contract")
|
|
||||||
dumpBalancesCmd.Flags().Bool(dumpBalancesUseScriptHashFlag, false, "Use script-hash format for addresses")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(updateContractsCmd)
|
|
||||||
updateContractsCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
updateContractsCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
updateContractsCmd.Flags().String(contractsInitFlag, "", "Path to archive with compiled FrostFS contracts (default fetched from latest github release)")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(dumpContainersCmd)
|
|
||||||
dumpContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
dumpContainersCmd.Flags().String(containerDumpFlag, "", "File where to save dumped containers")
|
|
||||||
dumpContainersCmd.Flags().String(containerContractFlag, "", "Container contract hash (for networks without NNS)")
|
|
||||||
dumpContainersCmd.Flags().StringSlice(containerIDsFlag, nil, "Containers to dump")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(restoreContainersCmd)
|
|
||||||
restoreContainersCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
restoreContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
restoreContainersCmd.Flags().String(containerDumpFlag, "", "File to restore containers from")
|
|
||||||
restoreContainersCmd.Flags().StringSlice(containerIDsFlag, nil, "Containers to restore")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(listContainersCmd)
|
|
||||||
listContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
listContainersCmd.Flags().String(containerContractFlag, "", "Container contract hash (for networks without NNS)")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(refillGasCmd)
|
|
||||||
refillGasCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
|
||||||
refillGasCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
refillGasCmd.Flags().String(storageWalletFlag, "", "Path to storage node wallet")
|
|
||||||
refillGasCmd.Flags().String(walletAddressFlag, "", "Address of wallet")
|
|
||||||
refillGasCmd.Flags().String(refillGasAmountFlag, "", "Additional amount of GAS to transfer")
|
|
||||||
refillGasCmd.MarkFlagsMutuallyExclusive(walletAddressFlag, storageWalletFlag)
|
|
||||||
|
|
||||||
RootCmd.AddCommand(cmdSubnet)
|
|
||||||
|
|
||||||
RootCmd.AddCommand(depositNotaryCmd)
|
|
||||||
depositNotaryCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
depositNotaryCmd.Flags().String(storageWalletFlag, "", "Path to storage node wallet")
|
|
||||||
depositNotaryCmd.Flags().String(walletAccountFlag, "", "Wallet account address")
|
|
||||||
depositNotaryCmd.Flags().String(refillGasAmountFlag, "", "Amount of GAS to deposit")
|
|
||||||
depositNotaryCmd.Flags().String(notaryDepositTillFlag, "", "Notary deposit duration in blocks")
|
|
||||||
|
|
||||||
RootCmd.AddCommand(netmapCandidatesCmd)
|
|
||||||
netmapCandidatesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,8 +307,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// common executor cmdSubnetAdminAdd and cmdSubnetAdminRemove commands.
|
// common executor cmdSubnetAdminAdd and cmdSubnetAdminRemove commands.
|
||||||
//
|
|
||||||
// nolint: funlen
|
|
||||||
func manageSubnetAdmins(cmd *cobra.Command, rm bool) error {
|
func manageSubnetAdmins(cmd *cobra.Command, rm bool) error {
|
||||||
// read private key
|
// read private key
|
||||||
var key keys.PrivateKey
|
var key keys.PrivateKey
|
||||||
|
@ -341,17 +339,19 @@ func manageSubnetAdmins(cmd *cobra.Command, rm bool) error {
|
||||||
return fmt.Errorf("admin key format: %w", err)
|
return fmt.Errorf("admin key format: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare call parameters
|
return invokeMethodWithParams(cmd, id, rm, binAdminKey, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func invokeMethodWithParams(cmd *cobra.Command, id subnetid.ID, rm bool, binAdminKey []byte, key keys.PrivateKey) error {
|
||||||
prm := make([]any, 0, 3)
|
prm := make([]any, 0, 3)
|
||||||
prm = append(prm, id.Marshal())
|
prm = append(prm, id.Marshal())
|
||||||
|
|
||||||
var method string
|
var method string
|
||||||
|
|
||||||
if viper.GetBool(flagSubnetAdminClient) {
|
if viper.GetBool(flagSubnetAdminClient) {
|
||||||
// read group ID and encode it
|
|
||||||
var groupID internal.SubnetClientGroupID
|
var groupID internal.SubnetClientGroupID
|
||||||
|
|
||||||
err = groupID.UnmarshalText([]byte(viper.GetString(flagSubnetAdminAddGroup)))
|
err := groupID.UnmarshalText([]byte(viper.GetString(flagSubnetAdminAddGroup)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("decode group ID text: %w", err)
|
return fmt.Errorf("decode group ID text: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -378,7 +378,7 @@ func manageSubnetAdmins(cmd *cobra.Command, rm bool) error {
|
||||||
|
|
||||||
prm = append(prm, binAdminKey)
|
prm = append(prm, binAdminKey)
|
||||||
|
|
||||||
err = invokeMethod(key, false, method, prm...)
|
err := invokeMethod(key, false, method, prm...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("morph invocation: %w", err)
|
return fmt.Errorf("morph invocation: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -653,55 +653,14 @@ func addCommandInheritPreRun(par *cobra.Command, subs ...*cobra.Command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// registers flags and binds sub-commands for subnet commands.
|
// registers flags and binds sub-commands for subnet commands.
|
||||||
//
|
|
||||||
// nolint: funlen
|
|
||||||
func init() {
|
func init() {
|
||||||
cmdSubnetCreate.Flags().StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
initCreateSubnetFlags()
|
||||||
_ = cmdSubnetCreate.MarkFlagRequired(flagSubnetWallet)
|
initGetSubnetFlags()
|
||||||
cmdSubnetCreate.Flags().StringP(flagSubnetAddress, "a", "", "Address in the wallet, optional")
|
initRemoveSubnetFlags()
|
||||||
|
initSubnetAdminFlags()
|
||||||
// get subnet flags
|
initSubnetAdminAddFlags()
|
||||||
cmdSubnetGet.Flags().String(flagSubnetGetID, "", "ID of the subnet to read")
|
initSubnetAdminRemoveFlags()
|
||||||
_ = cmdSubnetAdminAdd.MarkFlagRequired(flagSubnetGetID)
|
initClientManagementFlags()
|
||||||
|
|
||||||
// remove subnet flags
|
|
||||||
cmdSubnetRemove.Flags().String(flagSubnetRemoveID, "", "ID of the subnet to remove")
|
|
||||||
_ = cmdSubnetRemove.MarkFlagRequired(flagSubnetRemoveID)
|
|
||||||
cmdSubnetRemove.Flags().StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
|
||||||
_ = cmdSubnetRemove.MarkFlagRequired(flagSubnetWallet)
|
|
||||||
cmdSubnetRemove.Flags().StringP(flagSubnetAddress, "a", "", "Address in the wallet, optional")
|
|
||||||
|
|
||||||
// subnet administer flags
|
|
||||||
adminFlags := cmdSubnetAdmin.PersistentFlags()
|
|
||||||
adminFlags.String(flagSubnetAdminSubnet, "", "ID of the subnet to manage administrators")
|
|
||||||
_ = cmdSubnetAdmin.MarkFlagRequired(flagSubnetAdminSubnet)
|
|
||||||
adminFlags.String(flagSubnetAdminID, "", "Hex-encoded public key of the admin")
|
|
||||||
_ = cmdSubnetAdmin.MarkFlagRequired(flagSubnetAdminID)
|
|
||||||
adminFlags.StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
|
||||||
_ = cmdSubnetAdmin.MarkFlagRequired(flagSubnetWallet)
|
|
||||||
adminFlags.StringP(flagSubnetAddress, "a", "", "Address in the wallet, optional")
|
|
||||||
|
|
||||||
// add admin flags
|
|
||||||
cmdSubnetAdminAddFlags := cmdSubnetAdminAdd.Flags()
|
|
||||||
cmdSubnetAdminAddFlags.String(flagSubnetAdminAddGroup, "", fmt.Sprintf(
|
|
||||||
"Client group ID in text format (needed with --%s only)", flagSubnetAdminClient))
|
|
||||||
cmdSubnetAdminAddFlags.Bool(flagSubnetAdminClient, false, "Add client admin instead of node one")
|
|
||||||
|
|
||||||
// remove admin flags
|
|
||||||
cmdSubnetAdminRemoveFlags := cmdSubnetAdminRemove.Flags()
|
|
||||||
cmdSubnetAdminRemoveFlags.Bool(flagSubnetAdminClient, false, "Remove client admin instead of node one")
|
|
||||||
|
|
||||||
// client managements flags
|
|
||||||
clientFlags := cmdSubnetClient.PersistentFlags()
|
|
||||||
clientFlags.String(flagSubnetClientSubnet, "", "ID of the subnet to be managed")
|
|
||||||
_ = cmdSubnetClient.MarkFlagRequired(flagSubnetClientSubnet)
|
|
||||||
clientFlags.String(flagSubnetClientGroup, "", "ID of the client group to work with")
|
|
||||||
_ = cmdSubnetClient.MarkFlagRequired(flagSubnetClientGroup)
|
|
||||||
clientFlags.String(flagSubnetClientID, "", "Client's user ID in FrostFS system in text format")
|
|
||||||
_ = cmdSubnetClient.MarkFlagRequired(flagSubnetClientID)
|
|
||||||
clientFlags.StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
|
||||||
_ = cmdSubnetClient.MarkFlagRequired(flagSubnetWallet)
|
|
||||||
clientFlags.StringP(flagSubnetAddress, "a", "", "Address in the wallet, optional")
|
|
||||||
|
|
||||||
// add all admin managing commands to corresponding command section
|
// add all admin managing commands to corresponding command section
|
||||||
addCommandInheritPreRun(cmdSubnetAdmin,
|
addCommandInheritPreRun(cmdSubnetAdmin,
|
||||||
|
@ -715,14 +674,7 @@ func init() {
|
||||||
cmdSubnetClientRemove,
|
cmdSubnetClientRemove,
|
||||||
)
|
)
|
||||||
|
|
||||||
// subnet node flags
|
initSubnetNodeFlags()
|
||||||
nodeFlags := cmdSubnetNode.PersistentFlags()
|
|
||||||
nodeFlags.StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
|
||||||
_ = cmdSubnetNode.MarkFlagRequired(flagSubnetWallet)
|
|
||||||
nodeFlags.String(flagSubnetNode, "", "Hex-encoded public key of the node")
|
|
||||||
_ = cmdSubnetNode.MarkFlagRequired(flagSubnetNode)
|
|
||||||
nodeFlags.String(flagSubnetNodeSubnet, "", "ID of the subnet to manage nodes")
|
|
||||||
_ = cmdSubnetNode.MarkFlagRequired(flagSubnetNodeSubnet)
|
|
||||||
|
|
||||||
// add all node managing commands to corresponding command section
|
// add all node managing commands to corresponding command section
|
||||||
addCommandInheritPreRun(cmdSubnetNode,
|
addCommandInheritPreRun(cmdSubnetNode,
|
||||||
|
@ -730,10 +682,7 @@ func init() {
|
||||||
cmdSubnetNodeRemove,
|
cmdSubnetNodeRemove,
|
||||||
)
|
)
|
||||||
|
|
||||||
// subnet global flags
|
initSubnetGlobalFlags()
|
||||||
cmdSubnetFlags := cmdSubnet.PersistentFlags()
|
|
||||||
cmdSubnetFlags.StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
|
||||||
_ = cmdSubnet.MarkFlagRequired(endpointFlag)
|
|
||||||
|
|
||||||
// add all subnet commands to corresponding command section
|
// add all subnet commands to corresponding command section
|
||||||
addCommandInheritPreRun(cmdSubnet,
|
addCommandInheritPreRun(cmdSubnet,
|
||||||
|
@ -746,6 +695,77 @@ func init() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initSubnetGlobalFlags() {
|
||||||
|
cmdSubnetFlags := cmdSubnet.PersistentFlags()
|
||||||
|
cmdSubnetFlags.StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||||
|
_ = cmdSubnet.MarkFlagRequired(endpointFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSubnetNodeFlags() {
|
||||||
|
nodeFlags := cmdSubnetNode.PersistentFlags()
|
||||||
|
nodeFlags.StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
||||||
|
_ = cmdSubnetNode.MarkFlagRequired(flagSubnetWallet)
|
||||||
|
nodeFlags.String(flagSubnetNode, "", "Hex-encoded public key of the node")
|
||||||
|
_ = cmdSubnetNode.MarkFlagRequired(flagSubnetNode)
|
||||||
|
nodeFlags.String(flagSubnetNodeSubnet, "", "ID of the subnet to manage nodes")
|
||||||
|
_ = cmdSubnetNode.MarkFlagRequired(flagSubnetNodeSubnet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initClientManagementFlags() {
|
||||||
|
clientFlags := cmdSubnetClient.PersistentFlags()
|
||||||
|
clientFlags.String(flagSubnetClientSubnet, "", "ID of the subnet to be managed")
|
||||||
|
_ = cmdSubnetClient.MarkFlagRequired(flagSubnetClientSubnet)
|
||||||
|
clientFlags.String(flagSubnetClientGroup, "", "ID of the client group to work with")
|
||||||
|
_ = cmdSubnetClient.MarkFlagRequired(flagSubnetClientGroup)
|
||||||
|
clientFlags.String(flagSubnetClientID, "", "Client's user ID in FrostFS system in text format")
|
||||||
|
_ = cmdSubnetClient.MarkFlagRequired(flagSubnetClientID)
|
||||||
|
clientFlags.StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
||||||
|
_ = cmdSubnetClient.MarkFlagRequired(flagSubnetWallet)
|
||||||
|
clientFlags.StringP(flagSubnetAddress, "a", "", "Address in the wallet, optional")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSubnetAdminRemoveFlags() {
|
||||||
|
cmdSubnetAdminRemoveFlags := cmdSubnetAdminRemove.Flags()
|
||||||
|
cmdSubnetAdminRemoveFlags.Bool(flagSubnetAdminClient, false, "Remove client admin instead of node one")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSubnetAdminAddFlags() {
|
||||||
|
cmdSubnetAdminAddFlags := cmdSubnetAdminAdd.Flags()
|
||||||
|
cmdSubnetAdminAddFlags.String(flagSubnetAdminAddGroup, "", fmt.Sprintf(
|
||||||
|
"Client group ID in text format (needed with --%s only)", flagSubnetAdminClient))
|
||||||
|
cmdSubnetAdminAddFlags.Bool(flagSubnetAdminClient, false, "Add client admin instead of node one")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSubnetAdminFlags() {
|
||||||
|
adminFlags := cmdSubnetAdmin.PersistentFlags()
|
||||||
|
adminFlags.String(flagSubnetAdminSubnet, "", "ID of the subnet to manage administrators")
|
||||||
|
_ = cmdSubnetAdmin.MarkFlagRequired(flagSubnetAdminSubnet)
|
||||||
|
adminFlags.String(flagSubnetAdminID, "", "Hex-encoded public key of the admin")
|
||||||
|
_ = cmdSubnetAdmin.MarkFlagRequired(flagSubnetAdminID)
|
||||||
|
adminFlags.StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
||||||
|
_ = cmdSubnetAdmin.MarkFlagRequired(flagSubnetWallet)
|
||||||
|
adminFlags.StringP(flagSubnetAddress, "a", "", "Address in the wallet, optional")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initRemoveSubnetFlags() {
|
||||||
|
cmdSubnetRemove.Flags().String(flagSubnetRemoveID, "", "ID of the subnet to remove")
|
||||||
|
_ = cmdSubnetRemove.MarkFlagRequired(flagSubnetRemoveID)
|
||||||
|
cmdSubnetRemove.Flags().StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
||||||
|
_ = cmdSubnetRemove.MarkFlagRequired(flagSubnetWallet)
|
||||||
|
cmdSubnetRemove.Flags().StringP(flagSubnetAddress, "a", "", "Address in the wallet, optional")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initGetSubnetFlags() {
|
||||||
|
cmdSubnetGet.Flags().String(flagSubnetGetID, "", "ID of the subnet to read")
|
||||||
|
_ = cmdSubnetAdminAdd.MarkFlagRequired(flagSubnetGetID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initCreateSubnetFlags() {
|
||||||
|
cmdSubnetCreate.Flags().StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
||||||
|
_ = cmdSubnetCreate.MarkFlagRequired(flagSubnetWallet)
|
||||||
|
cmdSubnetCreate.Flags().StringP(flagSubnetAddress, "a", "", "Address in the wallet, optional")
|
||||||
|
}
|
||||||
|
|
||||||
func testInvokeMethod(key keys.PrivateKey, method string, args ...any) ([]stackitem.Item, error) {
|
func testInvokeMethod(key keys.PrivateKey, method string, args ...any) ([]stackitem.Item, error) {
|
||||||
c, err := getN3Client(viper.GetViper())
|
c, err := getN3Client(viper.GetViper())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -872,7 +892,6 @@ func invokeNonNotary(c Client, key keys.PrivateKey, method string, args ...any)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: funlen
|
|
||||||
func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.Uint160, args ...any) error {
|
func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.Uint160, args ...any) error {
|
||||||
nnsCs, err := c.GetContractStateByID(1)
|
nnsCs, err := c.GetContractStateByID(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -901,13 +920,7 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.
|
||||||
return fmt.Errorf("subnet hash resolving: %w", err)
|
return fmt.Errorf("subnet hash resolving: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// make test invocation of the method
|
test, err := makeTestInvocation(inv, subnetHash, method, args)
|
||||||
test, err := inv.Call(subnetHash, method, args...)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("test invocation: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = checkInvocationResults(test)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -923,7 +936,26 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.
|
||||||
return fmt.Errorf("blockchain height: %w", err)
|
return fmt.Errorf("blockchain height: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signersNumber := uint8(smartcontract.GetDefaultHonestNodeCount(len(alphabet)) + 1) // alphabet multisig + key signature
|
return createAndPushTransaction(alphabet, test, bc, cosigners, c, key, multisigAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeTestInvocation(inv *invoker.Invoker, subnetHash util.Uint160, method string, args []any) (*result.Invoke, error) {
|
||||||
|
test, err := inv.Call(subnetHash, method, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("test invocation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = checkInvocationResults(test)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return test, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAndPushTransaction(alphabet keys.PublicKeys, test *result.Invoke, blockCount uint32, cosigners []transaction.Signer,
|
||||||
|
client Client, key keys.PrivateKey, multisigAccount *wallet.Account) error {
|
||||||
|
// alphabet multisig + key signature
|
||||||
|
signersNumber := uint8(smartcontract.GetDefaultHonestNodeCount(len(alphabet)) + 1)
|
||||||
|
|
||||||
// notaryRequestValidity is number of blocks during
|
// notaryRequestValidity is number of blocks during
|
||||||
// witch notary request is considered valid
|
// witch notary request is considered valid
|
||||||
|
@ -932,7 +964,7 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.
|
||||||
mainTx := &transaction.Transaction{
|
mainTx := &transaction.Transaction{
|
||||||
Nonce: rand.Uint32(),
|
Nonce: rand.Uint32(),
|
||||||
SystemFee: test.GasConsumed,
|
SystemFee: test.GasConsumed,
|
||||||
ValidUntilBlock: bc + notaryRequestValidity,
|
ValidUntilBlock: blockCount + notaryRequestValidity,
|
||||||
Script: test.Script,
|
Script: test.Script,
|
||||||
Attributes: []transaction.Attribute{
|
Attributes: []transaction.Attribute{
|
||||||
{
|
{
|
||||||
|
@ -943,7 +975,7 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.
|
||||||
Signers: cosigners,
|
Signers: cosigners,
|
||||||
}
|
}
|
||||||
|
|
||||||
notaryFee, err := c.CalculateNotaryFee(signersNumber)
|
notaryFee, err := client.CalculateNotaryFee(signersNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -951,14 +983,14 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.
|
||||||
acc := wallet.NewAccountFromPrivateKey(&key)
|
acc := wallet.NewAccountFromPrivateKey(&key)
|
||||||
aa := notaryAccounts(multisigAccount, acc)
|
aa := notaryAccounts(multisigAccount, acc)
|
||||||
|
|
||||||
err = c.AddNetworkFee(mainTx, notaryFee, aa...)
|
err = client.AddNetworkFee(mainTx, notaryFee, aa...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("notary network fee adding: %w", err)
|
return fmt.Errorf("notary network fee adding: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mainTx.Scripts = notaryWitnesses(c, multisigAccount, acc, mainTx)
|
mainTx.Scripts = notaryWitnesses(client, multisigAccount, acc, mainTx)
|
||||||
|
|
||||||
_, err = c.SignAndPushP2PNotaryRequest(mainTx,
|
_, err = client.SignAndPushP2PNotaryRequest(mainTx,
|
||||||
[]byte{byte(opcode.RET)},
|
[]byte{byte(opcode.RET)},
|
||||||
-1,
|
-1,
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -79,17 +79,8 @@ type config struct {
|
||||||
MetabasePath string
|
MetabasePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: funlen
|
|
||||||
func storageConfig(cmd *cobra.Command, args []string) {
|
func storageConfig(cmd *cobra.Command, args []string) {
|
||||||
var outPath string
|
outPath := getOutputPath(args)
|
||||||
if len(args) != 0 {
|
|
||||||
outPath = args[0]
|
|
||||||
} else {
|
|
||||||
outPath = getPath("File to write config at [./config.yml]: ")
|
|
||||||
if outPath == "" {
|
|
||||||
outPath = "./config.yml"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
historyPath := filepath.Join(os.TempDir(), "frostfs-adm.history")
|
historyPath := filepath.Join(os.TempDir(), "frostfs-adm.history")
|
||||||
readline.SetHistoryPath(historyPath)
|
readline.SetHistoryPath(historyPath)
|
||||||
|
@ -104,14 +95,7 @@ func storageConfig(cmd *cobra.Command, args []string) {
|
||||||
w, err := wallet.NewWalletFromFile(c.Wallet.Path)
|
w, err := wallet.NewWalletFromFile(c.Wallet.Path)
|
||||||
fatalOnErr(err)
|
fatalOnErr(err)
|
||||||
|
|
||||||
c.Wallet.Account, _ = cmd.Flags().GetString(accountFlag)
|
fillWalletAccount(cmd, &c, w)
|
||||||
if c.Wallet.Account == "" {
|
|
||||||
addr := address.Uint160ToString(w.GetChangeAddress())
|
|
||||||
c.Wallet.Account = getWalletAccount(w, fmt.Sprintf("Wallet account [%s]: ", addr))
|
|
||||||
if c.Wallet.Account == "" {
|
|
||||||
c.Wallet.Account = addr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
accH, err := flags.ParseAddress(c.Wallet.Account)
|
accH, err := flags.ParseAddress(c.Wallet.Account)
|
||||||
fatalOnErr(err)
|
fatalOnErr(err)
|
||||||
|
@ -129,25 +113,44 @@ func storageConfig(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
c.AuthorizedKeys = append(c.AuthorizedKeys, hex.EncodeToString(acc.PrivateKey().PublicKey().Bytes()))
|
c.AuthorizedKeys = append(c.AuthorizedKeys, hex.EncodeToString(acc.PrivateKey().PublicKey().Bytes()))
|
||||||
|
|
||||||
var network string
|
network := readNetwork(cmd)
|
||||||
for {
|
|
||||||
network = getString("Choose network [mainnet]/testnet: ")
|
|
||||||
switch network {
|
|
||||||
case "":
|
|
||||||
network = "mainnet"
|
|
||||||
case "testnet", "mainnet":
|
|
||||||
default:
|
|
||||||
cmd.Println(`Network must be either "mainnet" or "testnet"`)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
c.MorphRPC = n3config[network].MorphRPC
|
c.MorphRPC = n3config[network].MorphRPC
|
||||||
|
|
||||||
depositGas(cmd, acc, network)
|
depositGas(cmd, acc, network)
|
||||||
|
|
||||||
c.Attribute.Locode = getString("UN-LOCODE attribute in [XX YYY] format: ")
|
c.Attribute.Locode = getString("UN-LOCODE attribute in [XX YYY] format: ")
|
||||||
|
|
||||||
|
endpoint := getDefaultEndpoint(cmd, &c)
|
||||||
|
c.Endpoint = getString(fmt.Sprintf("Listening address [%s]: ", endpoint))
|
||||||
|
if c.Endpoint == "" {
|
||||||
|
c.Endpoint = endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ControlEndpoint = getString(fmt.Sprintf("Listening address (control endpoint) [%s]: ", defaultControlEndpoint))
|
||||||
|
if c.ControlEndpoint == "" {
|
||||||
|
c.ControlEndpoint = defaultControlEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
c.TLSCert = getPath("TLS Certificate (optional): ")
|
||||||
|
if c.TLSCert != "" {
|
||||||
|
c.TLSKey = getPath("TLS Key: ")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Relay = getConfirmation(false, "Use node as a relay? yes/[no]: ")
|
||||||
|
if !c.Relay {
|
||||||
|
p := getPath("Path to the storage directory (all available storage will be used): ")
|
||||||
|
c.BlobstorPath = filepath.Join(p, "blob")
|
||||||
|
c.MetabasePath = filepath.Join(p, "meta")
|
||||||
|
}
|
||||||
|
|
||||||
|
out := applyTemplate(c)
|
||||||
|
fatalOnErr(os.WriteFile(outPath, out, 0644))
|
||||||
|
|
||||||
|
cmd.Println("Node is ready for work! Run `frostfs-node -config " + outPath + "`")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultEndpoint(cmd *cobra.Command, c *config) string {
|
||||||
var addr, port string
|
var addr, port string
|
||||||
for {
|
for {
|
||||||
c.AnnouncedAddress = getString("Publicly announced address: ")
|
c.AnnouncedAddress = getString("Publicly announced address: ")
|
||||||
|
@ -183,34 +186,46 @@ func storageConfig(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
return net.JoinHostPort(defaultDataEndpoint, port)
|
||||||
|
}
|
||||||
|
|
||||||
defaultAddr := net.JoinHostPort(defaultDataEndpoint, port)
|
func fillWalletAccount(cmd *cobra.Command, c *config, w *wallet.Wallet) {
|
||||||
c.Endpoint = getString(fmt.Sprintf("Listening address [%s]: ", defaultAddr))
|
c.Wallet.Account, _ = cmd.Flags().GetString(accountFlag)
|
||||||
if c.Endpoint == "" {
|
if c.Wallet.Account == "" {
|
||||||
c.Endpoint = defaultAddr
|
addr := address.Uint160ToString(w.GetChangeAddress())
|
||||||
|
c.Wallet.Account = getWalletAccount(w, fmt.Sprintf("Wallet account [%s]: ", addr))
|
||||||
|
if c.Wallet.Account == "" {
|
||||||
|
c.Wallet.Account = addr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
c.ControlEndpoint = getString(fmt.Sprintf("Listening address (control endpoint) [%s]: ", defaultControlEndpoint))
|
func readNetwork(cmd *cobra.Command) string {
|
||||||
if c.ControlEndpoint == "" {
|
var network string
|
||||||
c.ControlEndpoint = defaultControlEndpoint
|
for {
|
||||||
|
network = getString("Choose network [mainnet]/testnet: ")
|
||||||
|
switch network {
|
||||||
|
case "":
|
||||||
|
network = "mainnet"
|
||||||
|
case "testnet", "mainnet":
|
||||||
|
default:
|
||||||
|
cmd.Println(`Network must be either "mainnet" or "testnet"`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
return network
|
||||||
|
}
|
||||||
|
|
||||||
c.TLSCert = getPath("TLS Certificate (optional): ")
|
func getOutputPath(args []string) string {
|
||||||
if c.TLSCert != "" {
|
if len(args) != 0 {
|
||||||
c.TLSKey = getPath("TLS Key: ")
|
return args[0]
|
||||||
}
|
}
|
||||||
|
outPath := getPath("File to write config at [./config.yml]: ")
|
||||||
c.Relay = getConfirmation(false, "Use node as a relay? yes/[no]: ")
|
if outPath == "" {
|
||||||
if !c.Relay {
|
outPath = "./config.yml"
|
||||||
p := getPath("Path to the storage directory (all available storage will be used): ")
|
|
||||||
c.BlobstorPath = filepath.Join(p, "blob")
|
|
||||||
c.MetabasePath = filepath.Join(p, "meta")
|
|
||||||
}
|
}
|
||||||
|
return outPath
|
||||||
out := applyTemplate(c)
|
|
||||||
fatalOnErr(os.WriteFile(outPath, out, 0644))
|
|
||||||
|
|
||||||
cmd.Println("Node is ready for work! Run `frostfs-node -config " + outPath + "`")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWalletAccount(w *wallet.Wallet, prompt string) string {
|
func getWalletAccount(w *wallet.Wallet, prompt string) string {
|
||||||
|
|
Loading…
Reference in a new issue