frostfs-sdk-go/resolver/nns.go
Denis Kirillov 02f328a03c [#11] Add dns, nns resolver
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
2021-11-22 10:40:21 +03:00

130 lines
2.8 KiB
Go

package resolver
import (
"context"
"fmt"
nns "github.com/nspcc-dev/neo-go/examples/nft-nd-nns"
"github.com/nspcc-dev/neo-go/pkg/rpc/client"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
)
type NNSResolver interface {
// ResolveContainerName get record for domain name+'.container' from NNS contract.
ResolveContainerName(name string) (*cid.ID, error)
}
type nnsResolver struct {
rpc *client.Client
nnsHash util.Uint160
}
const (
resolve = "resolve"
containerDomain = ".container"
)
// NewNNSResolver creates resolver that can get records from NNS contract.
func NewNNSResolver(ctx context.Context, address string) (NNSResolver, error) {
rpc, err := client.New(ctx, address, client.Options{})
if err != nil {
return nil, err
}
if err = rpc.Init(); err != nil {
return nil, err
}
nnsContract, err := rpc.GetContractStateByID(1)
if err != nil {
return nil, err
}
return &nnsResolver{
rpc: rpc,
nnsHash: nnsContract.Hash,
}, nil
}
func (n *nnsResolver) ResolveContainerName(name string) (*cid.ID, error) {
res, err := n.rpc.InvokeFunction(n.nnsHash, resolve, []smartcontract.Parameter{
{
Type: smartcontract.StringType,
Value: name + containerDomain,
},
{
Type: smartcontract.IntegerType,
Value: int64(nns.TXT),
},
}, nil)
if err != nil {
return nil, err
}
if err = getInvocationError(res); err != nil {
return nil, err
}
arr, err := getArrString(res.Stack)
if err != nil {
return nil, err
}
cnrID := cid.New()
for _, rec := range arr {
if err = cnrID.Parse(rec); err != nil {
continue
}
return cnrID, nil
}
return nil, fmt.Errorf("not found")
}
func getArrString(st []stackitem.Item) ([]string, error) {
array, err := getArray(st)
if err != nil {
return nil, err
}
res := make([]string, len(array))
for i, item := range array {
bs, err := item.TryBytes()
if err != nil {
return nil, err
}
res[i] = string(bs)
}
return res, nil
}
func getArray(st []stackitem.Item) ([]stackitem.Item, error) {
index := len(st) - 1 // top stack element is last in the array
arr, err := st[index].Convert(stackitem.ArrayT)
if err != nil {
return nil, err
}
if _, ok := arr.(stackitem.Null); ok {
return nil, nil
}
iterator, ok := arr.Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("bad conversion")
}
return iterator, nil
}
func getInvocationError(result *result.Invoke) error {
if result.State != vm.HaltState.String() {
return fmt.Errorf("invocation failed: %s", result.FaultException)
}
if len(result.Stack) == 0 {
return fmt.Errorf("result stack is empty")
}
return nil
}