From b492201a8408b14871dd50f17eaa59053d3069a8 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Wed, 20 Oct 2021 16:17:01 +0300 Subject: [PATCH] [#936] morph/nns: Add record existence check Also, add ErrNNSRecordNotFound error that indicates that required hash is not presented in `NNS` contract. Signed-off-by: Pavel Karpy --- pkg/morph/client/nns.go | 46 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/pkg/morph/client/nns.go b/pkg/morph/client/nns.go index ede5bec1..f3543b72 100644 --- a/pkg/morph/client/nns.go +++ b/pkg/morph/client/nns.go @@ -32,6 +32,13 @@ const ( NNSReputationContractName = "reputation.neofs" ) +var ( + // ErrNNSRecordNotFound means that there is no such record in NNS contract. + ErrNNSRecordNotFound = errors.New("record has not been found in NNS contract") + + errEmptyResultStack = errors.New("returned result stack is empty") +) + // NNSAlphabetContractName returns contract name of the alphabet contract in NNS // based on alphabet index. func NNSAlphabetContractName(index int) string { @@ -40,6 +47,7 @@ func NNSAlphabetContractName(index int) string { // NNSContractAddress returns contract address script hash based on its name // in NNS contract. +// If script hash has not been found, returns ErrNNSRecordNotFound. func (c *Client) NNSContractAddress(name string) (sh util.Uint160, err error) { if c.multiClient != nil { return sh, c.multiClient.iterateClients(func(c *Client) error { @@ -61,6 +69,15 @@ func (c *Client) NNSContractAddress(name string) (sh util.Uint160, err error) { } func nnsResolve(c *client.Client, nnsHash util.Uint160, domain string) (util.Uint160, error) { + found, err := exists(c, nnsHash, domain) + if err != nil { + return util.Uint160{}, fmt.Errorf("could not check presence in NNS contract for %s: %w", domain, err) + } + + if !found { + return util.Uint160{}, ErrNNSRecordNotFound + } + result, err := c.InvokeFunction(nnsHash, "resolve", []smartcontract.Parameter{ { Type: smartcontract.StringType, @@ -78,7 +95,7 @@ func nnsResolve(c *client.Client, nnsHash util.Uint160, domain string) (util.Uin return util.Uint160{}, fmt.Errorf("invocation failed: %s", result.FaultException) } if len(result.Stack) == 0 { - return util.Uint160{}, errors.New("result stack is empty") + return util.Uint160{}, errEmptyResultStack } // Parse the result of resolving NNS record. @@ -97,3 +114,30 @@ func nnsResolve(c *client.Client, nnsHash util.Uint160, domain string) (util.Uin } return util.Uint160DecodeStringLE(string(bs)) } + +func exists(c *client.Client, nnsHash util.Uint160, domain string) (bool, error) { + result, err := c.InvokeFunction(nnsHash, "isAvailable", []smartcontract.Parameter{ + { + Type: smartcontract.StringType, + Value: domain, + }, + }, nil) + if err != nil { + return false, err + } + + if len(result.Stack) == 0 { + return false, errEmptyResultStack + } + + res := result.Stack[0] + + available, err := res.TryBool() + if err != nil { + return false, fmt.Errorf("malformed response: %w", err) + } + + // not available means that it is taken + // and, therefore, exists + return !available, nil +}