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
|
||||
}
|
||||
|
||||
// nolint: funlen
|
||||
func restoreContainers(cmd *cobra.Command, _ []string) error {
|
||||
filename, err := cmd.Flags().GetString(containerDumpFlag)
|
||||
if err != nil {
|
||||
|
@ -165,25 +164,14 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
|||
}
|
||||
defer wCtx.close()
|
||||
|
||||
nnsCs, err := wCtx.Client.GetContractStateByID(1)
|
||||
containers, err := parseContainers(filename)
|
||||
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 {
|
||||
return fmt.Errorf("can't fetch container contract hash: %w", 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)
|
||||
return err
|
||||
}
|
||||
|
||||
isOK, err := getCIDFilterFunc(cmd)
|
||||
|
@ -191,6 +179,15 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
|||
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()
|
||||
for _, cnt := range containers {
|
||||
hv := hash.Sha256(cnt.Value)
|
||||
|
@ -198,33 +195,18 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
|||
continue
|
||||
}
|
||||
bw.Reset()
|
||||
emit.AppCall(bw.BinWriter, ch, "get", callflag.All, hv.BytesBE())
|
||||
res, err := wCtx.Client.InvokeScript(bw.Bytes(), nil)
|
||||
restored, err := isContainerRestored(cmd, wCtx, ch, bw, hv)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't check if container is already restored: %w", err)
|
||||
return err
|
||||
}
|
||||
if len(res.Stack) == 0 {
|
||||
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)
|
||||
if restored {
|
||||
continue
|
||||
}
|
||||
|
||||
bw.Reset()
|
||||
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)
|
||||
}
|
||||
|
||||
putContainer(bw, ch, cnt)
|
||||
|
||||
if bw.Err != nil {
|
||||
panic(bw.Err)
|
||||
}
|
||||
|
@ -233,8 +215,67 @@ func restoreContainers(cmd *cobra.Command, _ []string) error {
|
|||
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.
|
||||
|
|
|
@ -57,7 +57,6 @@ func init() {
|
|||
ff.String(customZoneFlag, "frostfs", "Custom zone for NNS")
|
||||
}
|
||||
|
||||
// nolint: funlen
|
||||
func deployContractCmd(cmd *cobra.Command, args []string) error {
|
||||
v := viper.GetViper()
|
||||
c, err := newInitializeContext(cmd, v)
|
||||
|
@ -101,17 +100,34 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
|
|||
cs.Manifest.Name)
|
||||
}
|
||||
|
||||
w := io.NewBufBinWriter()
|
||||
if err := emitDeploymentArguments(w.BinWriter, args); err != nil {
|
||||
writer := io.NewBufBinWriter()
|
||||
if err := emitDeploymentArguments(writer.BinWriter, args); err != nil {
|
||||
return err
|
||||
}
|
||||
emit.Bytes(w.BinWriter, cs.RawManifest)
|
||||
emit.Bytes(w.BinWriter, cs.RawNEF)
|
||||
emit.Int(w.BinWriter, 3)
|
||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||
emit.AppCallNoArgs(w.BinWriter, callHash, method, callflag.All)
|
||||
emit.Opcodes(w.BinWriter, opcode.DROP) // contract state on stack
|
||||
emit.Bytes(writer.BinWriter, cs.RawManifest)
|
||||
emit.Bytes(writer.BinWriter, cs.RawNEF)
|
||||
emit.Int(writer.BinWriter, 3)
|
||||
emit.Opcodes(writer.BinWriter, opcode.PACK)
|
||||
emit.AppCallNoArgs(writer.BinWriter, callHash, method, callflag.All)
|
||||
emit.Opcodes(writer.BinWriter, opcode.DROP) // contract state on stack
|
||||
if !isUpdate {
|
||||
err := registerNNS(nnsCs, c, zone, domain, cs, writer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if writer.Err != nil {
|
||||
panic(fmt.Errorf("BUG: can't create deployment script: %w", writer.Err))
|
||||
}
|
||||
|
||||
if err := c.sendCommitteeTx(writer.Bytes(), false); err != nil {
|
||||
return err
|
||||
}
|
||||
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)
|
||||
|
@ -153,26 +169,17 @@ func deployContractCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
if bw.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))
|
||||
} 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)
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if w.Err != nil {
|
||||
panic(fmt.Errorf("BUG: can't create deployment script: %w", w.Err))
|
||||
}
|
||||
|
||||
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.awaitTx()
|
||||
return nil
|
||||
}
|
||||
|
||||
func emitDeploymentArguments(w *io.BinWriter, args []string) error {
|
||||
|
|
|
@ -108,7 +108,6 @@ func (c *initializeContext) close() {
|
|||
}
|
||||
}
|
||||
|
||||
// nolint: funlen
|
||||
func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContext, error) {
|
||||
walletDir := config.ResolveHomePath(viper.GetString(alphabetWalletsFlag))
|
||||
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"
|
||||
|
||||
var w *wallet.Wallet
|
||||
if needContracts {
|
||||
w, err = openContractWallet(v, cmd, walletDir)
|
||||
w, err = getWallet(cmd, v, needContracts, walletDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var c Client
|
||||
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)
|
||||
}
|
||||
c, err := createClient(cmd, v, wallets)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create N3 client: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
var ctrPath string
|
||||
if cmd.Name() == "init" {
|
||||
if viper.GetInt64(epochDurationInitFlag) <= 0 {
|
||||
return nil, fmt.Errorf("epoch duration must be positive")
|
||||
if err := validateInit(cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if viper.GetInt64(maxObjectSizeInitFlag) <= 0 {
|
||||
return nil, fmt.Errorf("max object size must be positive")
|
||||
}
|
||||
}
|
||||
|
||||
if needContracts {
|
||||
ctrPath, err = cmd.Flags().GetString(contractsInitFlag)
|
||||
ctrPath, err := getContractsPath(cmd, v, needContracts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid contracts path: %w", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := checkNotaryEnabled(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accounts := make([]*wallet.Account, len(wallets))
|
||||
for i, w := range wallets {
|
||||
acc, err := getWalletAccount(w, singleAccountName)
|
||||
accounts, err := createWalletAccounts(wallets)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("wallet %s is invalid (no single account): %w", w.Path(), err)
|
||||
}
|
||||
accounts[i] = acc
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cliCtx, err := defaultClientContext(c, committeeAcc)
|
||||
|
@ -207,6 +183,69 @@ func newInitializeContext(cmd *cobra.Command, v *viper.Viper) (*initializeContex
|
|||
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) {
|
||||
walletFiles, err := os.ReadDir(walletDir)
|
||||
if err != nil {
|
||||
|
|
|
@ -156,7 +156,6 @@ func (c *initializeContext) deployNNS(method string) error {
|
|||
return c.awaitTx()
|
||||
}
|
||||
|
||||
// nolint: funlen
|
||||
func (c *initializeContext) updateContracts() error {
|
||||
alphaCs := c.getContract(alphabetContract)
|
||||
|
||||
|
@ -168,8 +167,6 @@ func (c *initializeContext) updateContracts() error {
|
|||
|
||||
w := io2.NewBufBinWriter()
|
||||
|
||||
var keysParam []any
|
||||
|
||||
// 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.
|
||||
// The generated script is as following.
|
||||
|
@ -183,42 +180,36 @@ func (c *initializeContext) updateContracts() error {
|
|||
emit.Bytes(w.BinWriter, alphaCs.RawNEF)
|
||||
emit.Opcodes(w.BinWriter, opcode.STSFLD0)
|
||||
|
||||
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))
|
||||
keysParam, err := c.deployAlphabetAccounts(nnsHash, w, alphaCs)
|
||||
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()
|
||||
|
||||
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.AppCall(w.BinWriter, nnsHash, "getPrice", callflag.All)
|
||||
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())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
groupKey := c.ContractWallet.Accounts[0].PrivateKey().PublicKey()
|
||||
_, _, err = c.emitUpdateNNSGroupScript(w, nnsHash, groupKey)
|
||||
func (c *initializeContext) deployAlphabetAccounts(nnsHash util.Uint160, w *io2.BufBinWriter, alphaCs *contractState) ([]any, error) {
|
||||
var keysParam []any
|
||||
|
||||
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 err
|
||||
return nil, fmt.Errorf("can't resolve hash for contract update: %w", err)
|
||||
}
|
||||
c.Command.Printf("NNS: Set %s -> %s\n", morphClient.NNSGroupKeyName, hex.EncodeToString(groupKey.Bytes()))
|
||||
|
||||
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, 1)
|
||||
emit.Int(w.BinWriter, 3)
|
||||
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
|
||||
emit.AppCallNoArgs(w.BinWriter, ctrHash, updateMethodName, callflag.All)
|
||||
}
|
||||
return c.awaitTx()
|
||||
if err := c.sendCommitteeTx(w.Bytes(), false); err != nil {
|
||||
if !strings.Contains(err.Error(), common.ErrAlreadyUpdated) {
|
||||
return nil, err
|
||||
}
|
||||
c.Command.Println("Alphabet contracts are already updated.")
|
||||
}
|
||||
|
||||
return keysParam, nil
|
||||
}
|
||||
|
||||
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/crypto/keys"
|
||||
"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/gas"
|
||||
"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/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
@ -22,18 +24,10 @@ import (
|
|||
// https://github.com/nspcc-dev/neo-go/blob/master/pkg/core/native/notary.go#L48
|
||||
const defaultNotaryDepositLifetime = 5760
|
||||
|
||||
// nolint: funlen
|
||||
func depositNotary(cmd *cobra.Command, _ []string) error {
|
||||
p, err := cmd.Flags().GetString(storageWalletFlag)
|
||||
w, err := openWallet(cmd)
|
||||
if err != nil {
|
||||
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()
|
||||
|
@ -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())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -120,3 +118,18 @@ func depositNotary(cmd *cobra.Command, _ []string) error {
|
|||
|
||||
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() {
|
||||
RootCmd.AddCommand(generateAlphabetCmd)
|
||||
generateAlphabetCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||
generateAlphabetCmd.Flags().Uint(alphabetSizeFlag, 7, "Amount of alphabet wallets to generate")
|
||||
initGenerateAlphabetCmd()
|
||||
initInitCmd()
|
||||
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)
|
||||
initCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||
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().String(protoConfigPath, "", "Path to the consensus node configuration")
|
||||
initCmd.Flags().String(localDumpFlag, "", "Path to the blocks dump file")
|
||||
|
||||
RootCmd.AddCommand(deployCmd)
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
func initGenerateAlphabetCmd() {
|
||||
RootCmd.AddCommand(generateAlphabetCmd)
|
||||
generateAlphabetCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir")
|
||||
generateAlphabetCmd.Flags().Uint(alphabetSizeFlag, 7, "Amount of alphabet wallets to generate")
|
||||
}
|
||||
|
||||
func initDeployCmd() {
|
||||
RootCmd.AddCommand(deployCmd)
|
||||
}
|
||||
|
|
|
@ -307,8 +307,6 @@ const (
|
|||
)
|
||||
|
||||
// common executor cmdSubnetAdminAdd and cmdSubnetAdminRemove commands.
|
||||
//
|
||||
// nolint: funlen
|
||||
func manageSubnetAdmins(cmd *cobra.Command, rm bool) error {
|
||||
// read private key
|
||||
var key keys.PrivateKey
|
||||
|
@ -341,17 +339,19 @@ func manageSubnetAdmins(cmd *cobra.Command, rm bool) error {
|
|||
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 = append(prm, id.Marshal())
|
||||
|
||||
var method string
|
||||
|
||||
if viper.GetBool(flagSubnetAdminClient) {
|
||||
// read group ID and encode it
|
||||
var groupID internal.SubnetClientGroupID
|
||||
|
||||
err = groupID.UnmarshalText([]byte(viper.GetString(flagSubnetAdminAddGroup)))
|
||||
err := groupID.UnmarshalText([]byte(viper.GetString(flagSubnetAdminAddGroup)))
|
||||
if err != nil {
|
||||
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)
|
||||
|
||||
err = invokeMethod(key, false, method, prm...)
|
||||
err := invokeMethod(key, false, method, prm...)
|
||||
if err != nil {
|
||||
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.
|
||||
//
|
||||
// nolint: funlen
|
||||
func init() {
|
||||
cmdSubnetCreate.Flags().StringP(flagSubnetWallet, "w", "", "Path to file with wallet")
|
||||
_ = cmdSubnetCreate.MarkFlagRequired(flagSubnetWallet)
|
||||
cmdSubnetCreate.Flags().StringP(flagSubnetAddress, "a", "", "Address in the wallet, optional")
|
||||
|
||||
// get subnet flags
|
||||
cmdSubnetGet.Flags().String(flagSubnetGetID, "", "ID of the subnet to read")
|
||||
_ = cmdSubnetAdminAdd.MarkFlagRequired(flagSubnetGetID)
|
||||
|
||||
// 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")
|
||||
initCreateSubnetFlags()
|
||||
initGetSubnetFlags()
|
||||
initRemoveSubnetFlags()
|
||||
initSubnetAdminFlags()
|
||||
initSubnetAdminAddFlags()
|
||||
initSubnetAdminRemoveFlags()
|
||||
initClientManagementFlags()
|
||||
|
||||
// add all admin managing commands to corresponding command section
|
||||
addCommandInheritPreRun(cmdSubnetAdmin,
|
||||
|
@ -715,14 +674,7 @@ func init() {
|
|||
cmdSubnetClientRemove,
|
||||
)
|
||||
|
||||
// subnet node flags
|
||||
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)
|
||||
initSubnetNodeFlags()
|
||||
|
||||
// add all node managing commands to corresponding command section
|
||||
addCommandInheritPreRun(cmdSubnetNode,
|
||||
|
@ -730,10 +682,7 @@ func init() {
|
|||
cmdSubnetNodeRemove,
|
||||
)
|
||||
|
||||
// subnet global flags
|
||||
cmdSubnetFlags := cmdSubnet.PersistentFlags()
|
||||
cmdSubnetFlags.StringP(endpointFlag, "r", "", "N3 RPC node endpoint")
|
||||
_ = cmdSubnet.MarkFlagRequired(endpointFlag)
|
||||
initSubnetGlobalFlags()
|
||||
|
||||
// add all subnet commands to corresponding command section
|
||||
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) {
|
||||
c, err := getN3Client(viper.GetViper())
|
||||
if err != nil {
|
||||
|
@ -872,7 +892,6 @@ func invokeNonNotary(c Client, key keys.PrivateKey, method string, args ...any)
|
|||
return nil
|
||||
}
|
||||
|
||||
// nolint: funlen
|
||||
func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.Uint160, args ...any) error {
|
||||
nnsCs, err := c.GetContractStateByID(1)
|
||||
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)
|
||||
}
|
||||
|
||||
// make test invocation of the method
|
||||
test, err := inv.Call(subnetHash, method, args...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("test invocation: %w", err)
|
||||
}
|
||||
|
||||
err = checkInvocationResults(test)
|
||||
test, err := makeTestInvocation(inv, subnetHash, method, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -923,7 +936,26 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.
|
|||
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
|
||||
// witch notary request is considered valid
|
||||
|
@ -932,7 +964,7 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.
|
|||
mainTx := &transaction.Transaction{
|
||||
Nonce: rand.Uint32(),
|
||||
SystemFee: test.GasConsumed,
|
||||
ValidUntilBlock: bc + notaryRequestValidity,
|
||||
ValidUntilBlock: blockCount + notaryRequestValidity,
|
||||
Script: test.Script,
|
||||
Attributes: []transaction.Attribute{
|
||||
{
|
||||
|
@ -943,7 +975,7 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.
|
|||
Signers: cosigners,
|
||||
}
|
||||
|
||||
notaryFee, err := c.CalculateNotaryFee(signersNumber)
|
||||
notaryFee, err := client.CalculateNotaryFee(signersNumber)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -951,14 +983,14 @@ func invokeNotary(c Client, key keys.PrivateKey, method string, notaryHash util.
|
|||
acc := wallet.NewAccountFromPrivateKey(&key)
|
||||
aa := notaryAccounts(multisigAccount, acc)
|
||||
|
||||
err = c.AddNetworkFee(mainTx, notaryFee, aa...)
|
||||
err = client.AddNetworkFee(mainTx, notaryFee, aa...)
|
||||
if err != nil {
|
||||
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)},
|
||||
-1,
|
||||
0,
|
||||
|
|
|
@ -79,17 +79,8 @@ type config struct {
|
|||
MetabasePath string
|
||||
}
|
||||
|
||||
// nolint: funlen
|
||||
func storageConfig(cmd *cobra.Command, args []string) {
|
||||
var outPath string
|
||||
if len(args) != 0 {
|
||||
outPath = args[0]
|
||||
} else {
|
||||
outPath = getPath("File to write config at [./config.yml]: ")
|
||||
if outPath == "" {
|
||||
outPath = "./config.yml"
|
||||
}
|
||||
}
|
||||
outPath := getOutputPath(args)
|
||||
|
||||
historyPath := filepath.Join(os.TempDir(), "frostfs-adm.history")
|
||||
readline.SetHistoryPath(historyPath)
|
||||
|
@ -104,14 +95,7 @@ func storageConfig(cmd *cobra.Command, args []string) {
|
|||
w, err := wallet.NewWalletFromFile(c.Wallet.Path)
|
||||
fatalOnErr(err)
|
||||
|
||||
c.Wallet.Account, _ = cmd.Flags().GetString(accountFlag)
|
||||
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
|
||||
}
|
||||
}
|
||||
fillWalletAccount(cmd, &c, w)
|
||||
|
||||
accH, err := flags.ParseAddress(c.Wallet.Account)
|
||||
fatalOnErr(err)
|
||||
|
@ -129,25 +113,44 @@ func storageConfig(cmd *cobra.Command, args []string) {
|
|||
|
||||
c.AuthorizedKeys = append(c.AuthorizedKeys, hex.EncodeToString(acc.PrivateKey().PublicKey().Bytes()))
|
||||
|
||||
var network string
|
||||
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
|
||||
}
|
||||
network := readNetwork(cmd)
|
||||
|
||||
c.MorphRPC = n3config[network].MorphRPC
|
||||
|
||||
depositGas(cmd, acc, network)
|
||||
|
||||
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
|
||||
for {
|
||||
c.AnnouncedAddress = getString("Publicly announced address: ")
|
||||
|
@ -183,34 +186,46 @@ func storageConfig(cmd *cobra.Command, args []string) {
|
|||
|
||||
break
|
||||
}
|
||||
return net.JoinHostPort(defaultDataEndpoint, port)
|
||||
}
|
||||
|
||||
defaultAddr := net.JoinHostPort(defaultDataEndpoint, port)
|
||||
c.Endpoint = getString(fmt.Sprintf("Listening address [%s]: ", defaultAddr))
|
||||
if c.Endpoint == "" {
|
||||
c.Endpoint = defaultAddr
|
||||
func fillWalletAccount(cmd *cobra.Command, c *config, w *wallet.Wallet) {
|
||||
c.Wallet.Account, _ = cmd.Flags().GetString(accountFlag)
|
||||
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
|
||||
}
|
||||
|
||||
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: ")
|
||||
func readNetwork(cmd *cobra.Command) string {
|
||||
var network string
|
||||
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
|
||||
}
|
||||
|
||||
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")
|
||||
break
|
||||
}
|
||||
return network
|
||||
}
|
||||
|
||||
out := applyTemplate(c)
|
||||
fatalOnErr(os.WriteFile(outPath, out, 0644))
|
||||
|
||||
cmd.Println("Node is ready for work! Run `frostfs-node -config " + outPath + "`")
|
||||
func getOutputPath(args []string) string {
|
||||
if len(args) != 0 {
|
||||
return args[0]
|
||||
}
|
||||
outPath := getPath("File to write config at [./config.yml]: ")
|
||||
if outPath == "" {
|
||||
outPath = "./config.yml"
|
||||
}
|
||||
return outPath
|
||||
}
|
||||
|
||||
func getWalletAccount(w *wallet.Wallet, prompt string) string {
|
||||
|
|
Loading…
Reference in a new issue