adm: Add commands to manipulate with NNS
contract #981
|
@ -4,8 +4,6 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||
parseutil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util"
|
||||
|
@ -13,7 +11,6 @@ import (
|
|||
apechain "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||
policyengine "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine"
|
||||
morph "git.frostfs.info/TrueCloudLab/policy-engine/pkg/morph/policy"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -86,28 +83,19 @@ func parseChainName(cmd *cobra.Command) apechain.Name {
|
|||
return apeChainName
|
||||
}
|
||||
|
||||
func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *actor.Actor) {
|
||||
v := viper.GetViper()
|
||||
c, err := helper.GetN3Client(v)
|
||||
func newPolicyContractInterface(cmd *cobra.Command) (*morph.ContractStorage, *helper.LocalActor) {
|
||||
c, err := helper.GetN3Client(viper.GetViper())
|
||||
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||
|
||||
walletDir := config.ResolveHomePath(viper.GetString(commonflags.AlphabetWalletsFlag))
|
||||
wallets, err := helper.GetAlphabetWallets(v, walletDir)
|
||||
commonCmd.ExitOnErr(cmd, "unable to get alphabet wallets: %w", err)
|
||||
|
||||
committeeAcc, err := helper.GetWalletAccount(wallets[0], constants.CommitteeAccountName)
|
||||
commonCmd.ExitOnErr(cmd, "can't find committee account: %w", err)
|
||||
|
||||
ac, err := helper.NewActor(c, committeeAcc)
|
||||
ac, err := helper.NewLocalActor(cmd, c)
|
||||
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
||||
|
||||
inv := &ac.Invoker
|
||||
var ch util.Uint160
|
||||
r := management.NewReader(inv)
|
||||
r := management.NewReader(ac.Invoker)
|
||||
nnsCs, err := r.GetContractByID(1)
|
||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||
|
||||
ch, err = helper.NNSResolveHash(inv, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
||||
ch, err = helper.NNSResolveHash(ac.Invoker, nnsCs.Hash, helper.DomainOf(constants.PolicyContract))
|
||||
commonCmd.ExitOnErr(cmd, "unable to resolve policy contract hash: %w", err)
|
||||
|
||||
return morph.NewContractStorage(ac, ch), ac
|
||||
|
|
|
@ -29,6 +29,9 @@ const (
|
|||
ContractWalletPasswordKey = "contract"
|
||||
|
||||
FrostfsOpsEmail = "ops@frostfs.info"
|
||||
NNSRefreshDefVal = int64(3600)
|
||||
NNSRetryDefVal = int64(600)
|
||||
NNSTtlDefVal = int64(3600)
|
||||
|
||||
DefaultExpirationTime = 10 * 365 * 24 * time.Hour / time.Second
|
||||
|
||||
|
|
|
@ -148,12 +148,14 @@ func registerNNS(nnsCs *state.Contract, c *helper.InitializeContext, zone string
|
|||
|
||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||
zone, c.CommitteeAcc.Contract.ScriptHash(),
|
||||
constants.FrostfsOpsEmail, int64(3600), int64(600), int64(constants.DefaultExpirationTime), int64(3600))
|
||||
constants.FrostfsOpsEmail, constants.NNSRefreshDefVal, constants.NNSRetryDefVal,
|
||||
int64(constants.DefaultExpirationTime), constants.NNSTtlDefVal)
|
||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||
|
||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
||||
constants.FrostfsOpsEmail, int64(3600), int64(600), int64(constants.DefaultExpirationTime), int64(3600))
|
||||
constants.FrostfsOpsEmail, constants.NNSRefreshDefVal, constants.NNSRetryDefVal,
|
||||
int64(constants.DefaultExpirationTime), constants.NNSTtlDefVal)
|
||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||
} else {
|
||||
s, ok, err := c.NNSRegisterDomainScript(nnsCs.Hash, cs.Hash, domain)
|
||||
|
|
169
cmd/frostfs-adm/internal/modules/morph/helper/actor.go
Normal file
|
@ -0,0 +1,169 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/google/uuid"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// LocalActor is a kludge, do not use it outside of the morph commands.
|
||||
|
||||
type LocalActor struct {
|
||||
neoActor *actor.Actor
|
||||
fyrchik
commented
More like More like `LocalAuthor`?
acid-ant
commented
Renamed. Renamed.
|
||||
accounts []*wallet.Account
|
||||
Invoker *invoker.Invoker
|
||||
}
|
||||
|
||||
// NewLocalActor create LocalActor with accounts form provided wallets.
|
||||
// In case of empty wallets provided created actor with dummy account only for read operation.
|
||||
func NewLocalActor(cmd *cobra.Command, c actor.RPCActor) (*LocalActor, error) {
|
||||
walletDir := config.ResolveHomePath(viper.GetString(commonflags.AlphabetWalletsFlag))
|
||||
var act *actor.Actor
|
||||
var accounts []*wallet.Account
|
||||
if walletDir == "" {
|
||||
account, err := wallet.NewAccount()
|
||||
commonCmd.ExitOnErr(cmd, "unable to create dummy account: %w", err)
|
||||
act, err = actor.New(c, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: account.Contract.ScriptHash(),
|
||||
Scopes: transaction.Global,
|
||||
},
|
||||
Account: account,
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
wallets, err := GetAlphabetWallets(viper.GetViper(), walletDir)
|
||||
commonCmd.ExitOnErr(cmd, "unable to get alphabet wallets: %w", err)
|
||||
|
||||
for _, w := range wallets {
|
||||
acc, err := GetWalletAccount(w, constants.CommitteeAccountName)
|
||||
commonCmd.ExitOnErr(cmd, "can't find committee account: %w", err)
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
act, err = actor.New(c, []actor.SignerAccount{{
|
||||
Signer: transaction.Signer{
|
||||
Account: accounts[0].Contract.ScriptHash(),
|
||||
Scopes: transaction.Global,
|
||||
},
|
||||
Account: accounts[0],
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &LocalActor{
|
||||
neoActor: act,
|
||||
accounts: accounts,
|
||||
Invoker: &act.Invoker,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *LocalActor) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) {
|
||||
tx, err := a.neoActor.MakeCall(contract, method, params...)
|
||||
if err != nil {
|
||||
return util.Uint256{}, 0, err
|
||||
}
|
||||
err = a.resign(tx)
|
||||
if err != nil {
|
||||
return util.Uint256{}, 0, err
|
||||
}
|
||||
return a.neoActor.Send(tx)
|
||||
dstepanov-yadro
commented
Maybe rename Maybe rename `sign` -> `resign` ?
acid-ant
commented
Renamed. Renamed.
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
It is a kludge (why sign in the first place), but probably worth because the amount of code to copy was too high? Anyway, this makes our It is a kludge (why sign in the first place), but probably worth because the amount of code to copy was too high?
Anyway, this makes our `MakeRun` and `MakeCall` actually invalid if they are used from the outside e.g. in auto-generated bindings. What about making them `panic` to ensure we do not accidentally use them?
acid-ant
commented
Make sense, added Make sense, added `panic` for unused methods.
|
||||
}
|
||||
|
||||
func (a *LocalActor) SendRun(script []byte) (util.Uint256, uint32, error) {
|
||||
tx, err := a.neoActor.MakeRun(script)
|
||||
if err != nil {
|
||||
return util.Uint256{}, 0, err
|
||||
}
|
||||
err = a.resign(tx)
|
||||
if err != nil {
|
||||
return util.Uint256{}, 0, err
|
||||
}
|
||||
return a.neoActor.Send(tx)
|
||||
}
|
||||
|
||||
// resign is used to sign tx with committee accounts.
|
||||
// Inside the methods `MakeCall` and `SendRun` of the NeoGO's actor transaction is signing by committee account,
|
||||
// because actor uses committee wallet.
|
||||
// But it is not enough, need to sign with another committee accounts.
|
||||
func (a *LocalActor) resign(tx *transaction.Transaction) error {
|
||||
if len(a.accounts[0].Contract.Parameters) > 1 {
|
||||
// Use parameter context to avoid dealing with signature order.
|
||||
network := a.neoActor.GetNetwork()
|
||||
pc := context.NewParameterContext("", network, tx)
|
||||
h := a.accounts[0].Contract.ScriptHash()
|
||||
for _, acc := range a.accounts {
|
||||
priv := acc.PrivateKey()
|
||||
sign := priv.SignHashable(uint32(network), tx)
|
||||
if err := pc.AddSignature(h, acc.Contract, priv.PublicKey(), sign); err != nil {
|
||||
return fmt.Errorf("can't add signature: %w", err)
|
||||
}
|
||||
if len(pc.Items[h].Signatures) == len(acc.Contract.Parameters) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
w, err := pc.GetWitness(h)
|
||||
if err != nil {
|
||||
return fmt.Errorf("incomplete signature: %w", err)
|
||||
}
|
||||
tx.Scripts[0] = *w
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *LocalActor) Wait(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
|
||||
return a.neoActor.Wait(h, vub, err)
|
||||
}
|
||||
|
||||
func (a *LocalActor) Sender() util.Uint160 {
|
||||
return a.neoActor.Sender()
|
||||
}
|
||||
|
||||
func (a *LocalActor) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) {
|
||||
return a.neoActor.Call(contract, operation, params...)
|
||||
}
|
||||
|
||||
func (a *LocalActor) CallAndExpandIterator(_ util.Uint160, _ string, _ int, _ ...any) (*result.Invoke, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (a *LocalActor) TerminateSession(_ uuid.UUID) error {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (a *LocalActor) TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) {
|
||||
return a.neoActor.TraverseIterator(sessionID, iterator, num)
|
||||
}
|
||||
|
||||
func (a *LocalActor) MakeRun(_ []byte) (*transaction.Transaction, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (a *LocalActor) MakeUnsignedCall(_ util.Uint160, _ string, _ []transaction.Attribute, _ ...any) (*transaction.Transaction, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (a *LocalActor) MakeUnsignedRun(_ []byte, _ []transaction.Attribute) (*transaction.Transaction, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (a *LocalActor) MakeCall(_ util.Uint160, _ string, _ ...any) (*transaction.Transaction, error) {
|
||||
panic("unimplemented")
|
||||
}
|
|
@ -7,7 +7,9 @@ import (
|
|||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/config"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
|
@ -101,6 +103,16 @@ func GetWalletAccount(w *wallet.Wallet, typ string) (*wallet.Account, error) {
|
|||
return nil, fmt.Errorf("account for '%s' not found", typ)
|
||||
}
|
||||
|
||||
func GetComitteAcc(cmd *cobra.Command, v *viper.Viper) *wallet.Account {
|
||||
walletDir := config.ResolveHomePath(viper.GetString(commonflags.AlphabetWalletsFlag))
|
||||
wallets, err := GetAlphabetWallets(v, walletDir)
|
||||
commonCmd.ExitOnErr(cmd, "unable to get alphabet wallets: %w", err)
|
||||
|
||||
committeeAcc, err := GetWalletAccount(wallets[0], constants.CommitteeAccountName)
|
||||
commonCmd.ExitOnErr(cmd, "can't find committee account: %w", err)
|
||||
return committeeAcc
|
||||
}
|
||||
|
||||
func NNSResolve(inv *invoker.Invoker, nnsHash util.Uint160, domain string) (stackitem.Item, error) {
|
||||
return unwrap.Item(inv.Call(nnsHash, "resolve", domain, int64(nns.TXT)))
|
||||
}
|
||||
|
|
|
@ -478,7 +478,8 @@ func (c *InitializeContext) EmitUpdateNNSGroupScript(bw *io.BufBinWriter, nnsHas
|
|||
if isAvail {
|
||||
emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All,
|
||||
client.NNSGroupKeyName, c.CommitteeAcc.Contract.ScriptHash(),
|
||||
constants.FrostfsOpsEmail, int64(3600), int64(600), int64(constants.DefaultExpirationTime), int64(3600))
|
||||
constants.FrostfsOpsEmail, constants.NNSRefreshDefVal, constants.NNSRetryDefVal,
|
||||
int64(constants.DefaultExpirationTime), constants.NNSTtlDefVal)
|
||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||
}
|
||||
|
||||
|
@ -499,7 +500,8 @@ func (c *InitializeContext) NNSRegisterDomainScript(nnsHash, expectedHash util.U
|
|||
bw := io.NewBufBinWriter()
|
||||
emit.AppCall(bw.BinWriter, nnsHash, "register", callflag.All,
|
||||
domain, c.CommitteeAcc.Contract.ScriptHash(),
|
||||
constants.FrostfsOpsEmail, int64(3600), int64(600), int64(constants.DefaultExpirationTime), int64(3600))
|
||||
constants.FrostfsOpsEmail, constants.NNSRefreshDefVal, constants.NNSRetryDefVal,
|
||||
int64(constants.DefaultExpirationTime), constants.NNSTtlDefVal)
|
||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||
|
||||
if bw.Err != nil {
|
||||
|
|
|
@ -33,7 +33,8 @@ func setNNS(c *helper.InitializeContext) error {
|
|||
bw := io.NewBufBinWriter()
|
||||
emit.AppCall(bw.BinWriter, nnsCs.Hash, "register", callflag.All,
|
||||
"frostfs", c.CommitteeAcc.Contract.ScriptHash(),
|
||||
constants.FrostfsOpsEmail, int64(3600), int64(600), int64(constants.DefaultExpirationTime), int64(3600))
|
||||
constants.FrostfsOpsEmail, constants.NNSRefreshDefVal, constants.NNSRetryDefVal,
|
||||
int64(constants.DefaultExpirationTime), constants.NNSTtlDefVal)
|
||||
emit.Opcodes(bw.BinWriter, opcode.ASSERT)
|
||||
if err := c.SendCommitteeTx(bw.Bytes(), true); err != nil {
|
||||
return fmt.Errorf("can't add domain root to NNS: %w", err)
|
||||
|
|
25
cmd/frostfs-adm/internal/modules/morph/nns/helper.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package nns
|
||||
|
||||
import (
|
||||
client "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/nns"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/helper"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func getRPCClient(cmd *cobra.Command) (*client.Contract, *helper.LocalActor, util.Uint160) {
|
||||
v := viper.GetViper()
|
||||
c, err := helper.GetN3Client(v)
|
||||
commonCmd.ExitOnErr(cmd, "unable to create NEO rpc client: %w", err)
|
||||
|
||||
ac, err := helper.NewLocalActor(cmd, c)
|
||||
commonCmd.ExitOnErr(cmd, "can't create actor: %w", err)
|
||||
|
||||
r := management.NewReader(ac.Invoker)
|
||||
nnsCs, err := r.GetContractByID(1)
|
||||
commonCmd.ExitOnErr(cmd, "can't get NNS contract state: %w", err)
|
||||
return client.New(ac, nnsCs.Hash), ac, nnsCs.Hash
|
||||
}
|
148
cmd/frostfs-adm/internal/modules/morph/nns/record.go
Normal file
|
@ -0,0 +1,148 @@
|
|||
package nns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func initAddRecordCmd() {
|
||||
Cmd.AddCommand(addRecordCmd)
|
||||
addRecordCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
addRecordCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
addRecordCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
||||
addRecordCmd.Flags().String(nnsRecordTypeFlag, "", nnsRecordTypeFlagDesc)
|
||||
addRecordCmd.Flags().String(nnsRecordDataFlag, "", nnsRecordDataFlagDesc)
|
||||
dstepanov-yadro marked this conversation as resolved
Outdated
dstepanov-yadro
commented
Why Why `"Domain name service record data"` is not in constants?
acid-ant
commented
Thanks, moved to constant. Thanks, moved to constant.
|
||||
|
||||
_ = cobra.MarkFlagRequired(addRecordCmd.Flags(), nnsNameFlag)
|
||||
_ = cobra.MarkFlagRequired(addRecordCmd.Flags(), nnsRecordTypeFlag)
|
||||
_ = cobra.MarkFlagRequired(addRecordCmd.Flags(), nnsRecordDataFlag)
|
||||
}
|
||||
|
||||
func initGetRecordsCmd() {
|
||||
Cmd.AddCommand(getRecordsCmd)
|
||||
getRecordsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
getRecordsCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
getRecordsCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
||||
getRecordsCmd.Flags().String(nnsRecordTypeFlag, "", nnsRecordTypeFlagDesc)
|
||||
|
||||
_ = cobra.MarkFlagRequired(getRecordsCmd.Flags(), nnsNameFlag)
|
||||
}
|
||||
|
||||
func initDelRecordsCmd() {
|
||||
Cmd.AddCommand(delRecordsCmd)
|
||||
delRecordsCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
delRecordsCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
delRecordsCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
||||
delRecordsCmd.Flags().String(nnsRecordTypeFlag, "", nnsRecordTypeFlagDesc)
|
||||
|
||||
_ = cobra.MarkFlagRequired(delRecordsCmd.Flags(), nnsNameFlag)
|
||||
_ = cobra.MarkFlagRequired(delRecordsCmd.Flags(), nnsRecordTypeFlag)
|
||||
}
|
||||
|
||||
func addRecord(cmd *cobra.Command, _ []string) {
|
||||
c, actor, _ := getRPCClient(cmd)
|
||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||
data, _ := cmd.Flags().GetString(nnsRecordDataFlag)
|
||||
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
||||
typ, err := getRecordType(recordType)
|
||||
commonCmd.ExitOnErr(cmd, "unable to parse record type: %w", err)
|
||||
h, vub, err := c.AddRecord(name, typ, data)
|
||||
commonCmd.ExitOnErr(cmd, "unable to add record: %w", err)
|
||||
|
||||
cmd.Println("Waiting for transaction to persist...")
|
||||
_, err = actor.Wait(h, vub, err)
|
||||
commonCmd.ExitOnErr(cmd, "renew domain error: %w", err)
|
||||
cmd.Println("Record added successfully")
|
||||
}
|
||||
|
||||
func getRecords(cmd *cobra.Command, _ []string) {
|
||||
c, act, hash := getRPCClient(cmd)
|
||||
fyrchik
commented
`addRecord` and `getRecords` use the same function to receive RPC client
Won't we run in the same issue as #1009, namely reading alphabet wallets for methods which only read and do not sign anything?
acid-ant
commented
Good catch. Added ability for read operations ( Good catch. Added ability for read operations (`tokens`, `get-records`) execute without alphabet wallets.
|
||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
||||
if recordType == "" {
|
||||
sid, r, err := unwrap.SessionIterator(act.Invoker.Call(hash, "getAllRecords", name))
|
||||
commonCmd.ExitOnErr(cmd, "unable to get records: %w", err)
|
||||
defer func() {
|
||||
_ = act.Invoker.TerminateSession(sid)
|
||||
}()
|
||||
items, err := act.Invoker.TraverseIterator(sid, &r, 0)
|
||||
commonCmd.ExitOnErr(cmd, "unable to get records: %w", err)
|
||||
for len(items) != 0 {
|
||||
for j := range items {
|
||||
rs := items[j].Value().([]stackitem.Item)
|
||||
bs, err := rs[2].TryBytes()
|
||||
commonCmd.ExitOnErr(cmd, "unable to parse record state: %w", err)
|
||||
cmd.Printf("%s %s\n",
|
||||
recordTypeToString(nns.RecordType(rs[1].Value().(*big.Int).Int64())),
|
||||
string(bs))
|
||||
}
|
||||
aarifullin marked this conversation as resolved
Outdated
aarifullin
commented
Here is no check for Here is no check for `err` if `TraverseIterator` was not successful
acid-ant
commented
Thanks, missed that. Updated. Thanks, missed that. Updated.
|
||||
items, err = act.Invoker.TraverseIterator(sid, &r, 0)
|
||||
commonCmd.ExitOnErr(cmd, "unable to get records: %w", err)
|
||||
}
|
||||
} else {
|
||||
typ, err := getRecordType(recordType)
|
||||
commonCmd.ExitOnErr(cmd, "unable to parse record type: %w", err)
|
||||
items, err := c.GetRecords(name, typ)
|
||||
commonCmd.ExitOnErr(cmd, "unable to get records: %w", err)
|
||||
for _, item := range items {
|
||||
record, err := item.TryBytes()
|
||||
commonCmd.ExitOnErr(cmd, "unable to parse response: %w", err)
|
||||
cmd.Println(string(record))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func delRecords(cmd *cobra.Command, _ []string) {
|
||||
c, actor, _ := getRPCClient(cmd)
|
||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||
recordType, _ := cmd.Flags().GetString(nnsRecordTypeFlag)
|
||||
typ, err := getRecordType(recordType)
|
||||
commonCmd.ExitOnErr(cmd, "unable to parse record type: %w", err)
|
||||
h, vub, err := c.DeleteRecords(name, typ)
|
||||
commonCmd.ExitOnErr(cmd, "unable to delete records: %w", err)
|
||||
|
||||
cmd.Println("Waiting for transaction to persist...")
|
||||
_, err = actor.Wait(h, vub, err)
|
||||
commonCmd.ExitOnErr(cmd, "delete records error: %w", err)
|
||||
cmd.Println("Records removed successfully")
|
||||
}
|
||||
|
||||
dstepanov-yadro marked this conversation as resolved
Outdated
dstepanov-yadro
commented
`AAAA`?
acid-ant
commented
Ooops, fixed. Ooops, fixed.
|
||||
func getRecordType(recordType string) (*big.Int, error) {
|
||||
switch strings.ToUpper(recordType) {
|
||||
case "A":
|
||||
return big.NewInt(int64(nns.A)), nil
|
||||
case "CNAME":
|
||||
return big.NewInt(int64(nns.CNAME)), nil
|
||||
case "SOA":
|
||||
return big.NewInt(int64(nns.SOA)), nil
|
||||
case "TXT":
|
||||
return big.NewInt(int64(nns.TXT)), nil
|
||||
case "AAAA":
|
||||
return big.NewInt(int64(nns.AAAA)), nil
|
||||
}
|
||||
return nil, errors.New("unsupported record type")
|
||||
}
|
||||
|
||||
func recordTypeToString(rt nns.RecordType) string {
|
||||
switch rt {
|
||||
case nns.A:
|
||||
return "A"
|
||||
case nns.CNAME:
|
||||
return "CNAME"
|
||||
case nns.SOA:
|
||||
return "SOA"
|
||||
case nns.TXT:
|
||||
return "TXT"
|
||||
case nns.AAAA:
|
||||
return "AAAA"
|
||||
}
|
||||
return ""
|
||||
}
|
44
cmd/frostfs-adm/internal/modules/morph/nns/register.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package nns
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func initRegisterCmd() {
|
||||
Cmd.AddCommand(registerCmd)
|
||||
registerCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
registerCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
registerCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
||||
registerCmd.Flags().String(nnsEmailFlag, constants.FrostfsOpsEmail, "Domain owner email")
|
||||
registerCmd.Flags().Int64(nnsRefreshFlag, constants.NNSRefreshDefVal, "SOA record REFRESH parameter")
|
||||
registerCmd.Flags().Int64(nnsRetryFlag, constants.NNSRetryDefVal, "SOA record RETRY parameter")
|
||||
registerCmd.Flags().Int64(nnsExpireFlag, int64(constants.DefaultExpirationTime), "SOA record EXPIRE parameter")
|
||||
registerCmd.Flags().Int64(nnsTTLFlag, constants.NNSTtlDefVal, "SOA record TTL parameter")
|
||||
|
||||
_ = cobra.MarkFlagRequired(registerCmd.Flags(), nnsNameFlag)
|
||||
}
|
||||
|
||||
func registerDomain(cmd *cobra.Command, _ []string) {
|
||||
c, actor, _ := getRPCClient(cmd)
|
||||
|
||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||
email, _ := cmd.Flags().GetString(nnsEmailFlag)
|
||||
refresh, _ := cmd.Flags().GetInt64(nnsRefreshFlag)
|
||||
retry, _ := cmd.Flags().GetInt64(nnsRetryFlag)
|
||||
expire, _ := cmd.Flags().GetInt64(nnsExpireFlag)
|
||||
ttl, _ := cmd.Flags().GetInt64(nnsTTLFlag)
|
||||
|
||||
h, vub, err := c.Register(name, actor.Sender(), email, big.NewInt(refresh),
|
||||
big.NewInt(retry), big.NewInt(expire), big.NewInt(ttl))
|
||||
commonCmd.ExitOnErr(cmd, "unable to register domain: %w", err)
|
||||
|
||||
cmd.Println("Waiting for transaction to persist...")
|
||||
_, err = actor.Wait(h, vub, err)
|
||||
commonCmd.ExitOnErr(cmd, "register domain error: %w", err)
|
||||
cmd.Println("Domain registered successfully")
|
||||
}
|
26
cmd/frostfs-adm/internal/modules/morph/nns/renew.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package nns
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func initRenewCmd() {
|
||||
Cmd.AddCommand(renewCmd)
|
||||
renewCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
renewCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
renewCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
||||
}
|
||||
|
||||
func renewDomain(cmd *cobra.Command, _ []string) {
|
||||
c, actor, _ := getRPCClient(cmd)
|
||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||
h, vub, err := c.Renew(name)
|
||||
commonCmd.ExitOnErr(cmd, "unable to renew domain: %w", err)
|
||||
|
||||
cmd.Println("Waiting for transaction to persist...")
|
||||
_, err = actor.Wait(h, vub, err)
|
||||
commonCmd.ExitOnErr(cmd, "renew domain error: %w", err)
|
||||
cmd.Println("Domain renewed successfully")
|
||||
}
|
99
cmd/frostfs-adm/internal/modules/morph/nns/root.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package nns
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
nnsNameFlag = "name"
|
||||
nnsNameFlagDesc = "Domain name"
|
||||
nnsEmailFlag = "email"
|
||||
nnsRefreshFlag = "refresh"
|
||||
nnsRetryFlag = "retry"
|
||||
nnsExpireFlag = "expire"
|
||||
nnsTTLFlag = "ttl"
|
||||
nnsRecordTypeFlag = "type"
|
||||
nnsRecordTypeFlagDesc = "Domain name service record type(A|CNAME|SOA|TXT)"
|
||||
nnsRecordDataFlag = "data"
|
||||
nnsRecordDataFlagDesc = "Domain name service record data"
|
||||
)
|
||||
|
||||
var (
|
||||
Cmd = &cobra.Command{
|
||||
Use: "nns",
|
||||
Short: "Section for Neo Name Service (NNS)",
|
||||
}
|
||||
tokensCmd = &cobra.Command{
|
||||
Use: "tokens",
|
||||
Short: "List all registered domain names",
|
||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||
},
|
||||
Run: listTokens,
|
||||
}
|
||||
registerCmd = &cobra.Command{
|
||||
Use: "register",
|
||||
Short: "Registers a new domain",
|
||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
||||
},
|
||||
Run: registerDomain,
|
||||
}
|
||||
renewCmd = &cobra.Command{
|
||||
Use: "renew",
|
||||
Short: "Increases domain expiration date",
|
||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
||||
},
|
||||
Run: renewDomain,
|
||||
}
|
||||
updateCmd = &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Updates soa record",
|
||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
||||
},
|
||||
Run: updateSOA,
|
||||
}
|
||||
addRecordCmd = &cobra.Command{
|
||||
Use: "add-record",
|
||||
Short: "Adds a new record of the specified type to the provided domain",
|
||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
||||
},
|
||||
Run: addRecord,
|
||||
}
|
||||
getRecordsCmd = &cobra.Command{
|
||||
Use: "get-records",
|
||||
Short: "Returns domain record of the specified type",
|
||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||
},
|
||||
Run: getRecords,
|
||||
}
|
||||
delRecordsCmd = &cobra.Command{
|
||||
Use: "delete-records",
|
||||
Short: "Removes domain records with the specified type",
|
||||
PreRun: func(cmd *cobra.Command, _ []string) {
|
||||
_ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag))
|
||||
_ = viper.BindPFlag(commonflags.AlphabetWalletsFlag, cmd.Flags().Lookup(commonflags.AlphabetWalletsFlag))
|
||||
},
|
||||
Run: delRecords,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
initTokensCmd()
|
||||
initRegisterCmd()
|
||||
initRenewCmd()
|
||||
initUpdateCmd()
|
||||
initAddRecordCmd()
|
||||
initGetRecordsCmd()
|
||||
initDelRecordsCmd()
|
||||
}
|
24
cmd/frostfs-adm/internal/modules/morph/nns/tokens.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package nns
|
||||
|
||||
import (
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func initTokensCmd() {
|
||||
Cmd.AddCommand(tokensCmd)
|
||||
tokensCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
tokensCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
}
|
||||
|
||||
func listTokens(cmd *cobra.Command, _ []string) {
|
||||
c, _, _ := getRPCClient(cmd)
|
||||
it, err := c.Tokens()
|
||||
commonCmd.ExitOnErr(cmd, "unable to get tokens: %w", err)
|
||||
for toks, err := it.Next(10); err == nil && len(toks) > 0; toks, err = it.Next(10) {
|
||||
for _, token := range toks {
|
||||
cmd.Println(string(token))
|
||||
}
|
||||
}
|
||||
}
|
50
cmd/frostfs-adm/internal/modules/morph/nns/update.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package nns
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants"
|
||||
commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func initUpdateCmd() {
|
||||
Cmd.AddCommand(updateCmd)
|
||||
updateCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc)
|
||||
updateCmd.Flags().String(commonflags.AlphabetWalletsFlag, "", commonflags.AlphabetWalletsFlagDesc)
|
||||
updateCmd.Flags().String(nnsNameFlag, "", nnsNameFlagDesc)
|
||||
updateCmd.Flags().String(nnsEmailFlag, constants.FrostfsOpsEmail, "Domain owner email")
|
||||
updateCmd.Flags().Int64(nnsRefreshFlag, constants.NNSRefreshDefVal,
|
||||
"The number of seconds between update requests from secondary and slave name servers")
|
||||
fyrchik marked this conversation as resolved
Outdated
fyrchik
commented
We are not writing a DNS server here, I wonder, whether this description is of any help to anyone. Why not just refer to "SOA record REFRESH parameter" We are not writing a DNS server here, I wonder, whether this description is of any help to anyone. Why not just refer to "SOA record REFRESH parameter"
acid-ant
commented
Agree, made description shorter as you proposed. Agree, made description shorter as you proposed.
|
||||
updateCmd.Flags().Int64(nnsRetryFlag, constants.NNSRetryDefVal,
|
||||
"The number of seconds the secondary or slave will wait before retrying when the last attempt has failed")
|
||||
updateCmd.Flags().Int64(nnsExpireFlag, int64(constants.DefaultExpirationTime),
|
||||
"The number of seconds a master or slave will wait before considering the data stale "+
|
||||
"if it cannot reach the primary name server")
|
||||
updateCmd.Flags().Int64(nnsTTLFlag, constants.NNSTtlDefVal,
|
||||
"The number of seconds a domain name is cached locally before expiration and return to authoritative "+
|
||||
"nameservers for updated information")
|
||||
|
||||
_ = cobra.MarkFlagRequired(updateCmd.Flags(), nnsNameFlag)
|
||||
}
|
||||
|
||||
func updateSOA(cmd *cobra.Command, _ []string) {
|
||||
c, actor, _ := getRPCClient(cmd)
|
||||
|
||||
name, _ := cmd.Flags().GetString(nnsNameFlag)
|
||||
email, _ := cmd.Flags().GetString(nnsEmailFlag)
|
||||
refresh, _ := cmd.Flags().GetInt64(nnsRefreshFlag)
|
||||
retry, _ := cmd.Flags().GetInt64(nnsRetryFlag)
|
||||
expire, _ := cmd.Flags().GetInt64(nnsExpireFlag)
|
||||
ttl, _ := cmd.Flags().GetInt64(nnsTTLFlag)
|
||||
|
||||
h, vub, err := c.UpdateSOA(name, email, big.NewInt(refresh),
|
||||
big.NewInt(retry), big.NewInt(expire), big.NewInt(ttl))
|
||||
commonCmd.ExitOnErr(cmd, "unable to send transaction: %w", err)
|
||||
|
||||
cmd.Println("Waiting for transaction to persist...")
|
||||
_, err = actor.Wait(h, vub, err)
|
||||
commonCmd.ExitOnErr(cmd, "register domain error: %w", err)
|
||||
cmd.Println("SOA records updated successfully")
|
||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/generate"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/initialize"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/netmap"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/nns"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/node"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/notary"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/policy"
|
||||
|
@ -49,4 +50,5 @@ func init() {
|
|||
RootCmd.AddCommand(proxy.RemoveAccountCmd)
|
||||
|
||||
RootCmd.AddCommand(frostfsid.Cmd)
|
||||
RootCmd.AddCommand(nns.Cmd)
|
||||
}
|
||||
|
|
type is unnecessary, also "not implemented" is somewhat common, not sure we need constant here, like even in Go compiler:
Agree, updated.