diff --git a/pkg/rpc/client/native.go b/pkg/rpc/client/native.go index 770139790..8794af862 100644 --- a/pkg/rpc/client/native.go +++ b/pkg/rpc/client/native.go @@ -3,9 +3,11 @@ package client // Various non-policy things from native contracts. import ( + "errors" "fmt" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/core/native/nnsrecords" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -63,3 +65,54 @@ func (c *Client) GetDesignatedByRole(role noderoles.Role, index uint32) (keys.Pu } return topPublicKeysFromStack(result.Stack) } + +// NNSResolve invokes `resolve` method on a native NameService contract. +func (c *Client) NNSResolve(name string, typ nnsrecords.Type) (string, error) { + if typ == nnsrecords.CNAME { + return "", errors.New("can't resolve CNAME record type") + } + rmHash, err := c.GetNativeContractHash(nativenames.NameService) + if err != nil { + return "", fmt.Errorf("failed to get native NameService hash: %w", err) + } + result, err := c.InvokeFunction(rmHash, "resolve", []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: name, + }, + { + Type: smartcontract.IntegerType, + Value: int64(typ), + }, + }, nil) + if err != nil { + return "", err + } + err = getInvocationError(result) + if err != nil { + return "", fmt.Errorf("`resolve`: %w", err) + } + return topStringFromStack(result.Stack) +} + +// NNSIsAvailable invokes `isAvailable` method on a native NameService contract. +func (c *Client) NNSIsAvailable(name string) (bool, error) { + rmHash, err := c.GetNativeContractHash(nativenames.NameService) + if err != nil { + return false, fmt.Errorf("failed to get native NameService hash: %w", err) + } + result, err := c.InvokeFunction(rmHash, "isAvailable", []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: name, + }, + }, nil) + if err != nil { + return false, err + } + err = getInvocationError(result) + if err != nil { + return false, fmt.Errorf("`isAvailable`: %w", err) + } + return topBoolFromStack(result.Stack) +} diff --git a/pkg/rpc/server/client_test.go b/pkg/rpc/server/client_test.go index ebc323554..51c3fd6fe 100644 --- a/pkg/rpc/server/client_test.go +++ b/pkg/rpc/server/client_test.go @@ -8,6 +8,7 @@ import ( "github.com/nspcc-dev/neo-go/internal/testchain" "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/core/native/nnsrecords" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" @@ -532,3 +533,37 @@ func TestClient_NEP11(t *testing.T) { require.NoError(t, err) }) } + +func TestClient_NNS(t *testing.T) { + chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) + defer chain.Close() + defer rpcSrv.Shutdown() + + c, err := client.New(context.Background(), httpSrv.URL, client.Options{}) + require.NoError(t, err) + require.NoError(t, c.Init()) + + t.Run("NNSIsAvailable, false", func(t *testing.T) { + b, err := c.NNSIsAvailable("neo.com") + require.NoError(t, err) + require.Equal(t, false, b) + }) + t.Run("NNSIsAvailable, true", func(t *testing.T) { + b, err := c.NNSIsAvailable("neogo.com") + require.NoError(t, err) + require.Equal(t, true, b) + }) + t.Run("NNSResolve, good", func(t *testing.T) { + b, err := c.NNSResolve("neo.com", nnsrecords.A) + require.NoError(t, err) + require.Equal(t, "1.2.3.4", b) + }) + t.Run("NNSResolve, bad", func(t *testing.T) { + _, err := c.NNSResolve("neogo.com", nnsrecords.A) + require.Error(t, err) + }) + t.Run("NNSResolve, forbidden", func(t *testing.T) { + _, err := c.NNSResolve("neogo.com", nnsrecords.CNAME) + require.Error(t, err) + }) +}