[#11] Add dns, nns resolver
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
ca01b83adf
commit
02f328a03c
3 changed files with 171 additions and 0 deletions
BIN
go.sum
BIN
go.sum
Binary file not shown.
41
resolver/dns.go
Normal file
41
resolver/dns.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package resolver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
)
|
||||
|
||||
const (
|
||||
testnetDomain = ".containers.testnet.fs.neo.org"
|
||||
mainnetDomain = ".containers.fs.neo.org"
|
||||
)
|
||||
|
||||
// ResolveContainerTestnet request txt record name + '.containers.testnet.fs.neo.org' to default dns server.
|
||||
func ResolveContainerTestnet(name string) (*cid.ID, error) {
|
||||
return ResolveContainerDomainName(name + testnetDomain)
|
||||
}
|
||||
|
||||
// ResolveContainerMainnet request txt record name + '.containers.fs.neo.org' to default dns server.
|
||||
func ResolveContainerMainnet(name string) (*cid.ID, error) {
|
||||
return ResolveContainerDomainName(name + mainnetDomain)
|
||||
}
|
||||
|
||||
// ResolveContainerDomainName trys to resolve container domain name to container ID using system dns server.
|
||||
func ResolveContainerDomainName(domain string) (*cid.ID, error) {
|
||||
results, err := net.LookupTXT(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cnrID := cid.New()
|
||||
for _, res := range results {
|
||||
if err = cnrID.Parse(res); err != nil {
|
||||
continue
|
||||
}
|
||||
return cnrID, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
130
resolver/nns.go
Normal file
130
resolver/nns.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
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
|
||||
}
|
Loading…
Reference in a new issue