-
Notifications
You must be signed in to change notification settings - Fork 38
/
nns.go
145 lines (126 loc) · 4.26 KB
/
nns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package client
import (
"errors"
"fmt"
"strconv"
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/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"
)
const (
nnsContractID = 1 // NNS contract must be deployed first in side chain
// NNSAuditContractName is a name of the audit contract in NNS.
NNSAuditContractName = "audit.neofs"
// NNSBalanceContractName is a name of the balance contract in NNS.
NNSBalanceContractName = "balance.neofs"
// NNSContainerContractName is a name of the container contract in NNS.
NNSContainerContractName = "container.neofs"
// NNSNeoFSIDContractName is a name of the neofsid contract in NNS.
NNSNeoFSIDContractName = "neofsid.neofs"
// NNSNetmapContractName is a name of the netmap contract in NNS.
NNSNetmapContractName = "netmap.neofs"
// NNSProxyContractName is a name of the proxy contract in NNS.
NNSProxyContractName = "proxy.neofs"
// NNSReputationContractName is a name of the reputation contract in NNS.
NNSReputationContractName = "reputation.neofs"
// NNSSubnetworkContractName is a name of the subnet contract in NNS.
NNSSubnetworkContractName = "subnet.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 {
return "alphabet" + strconv.Itoa(index) + ".neofs"
}
// 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 {
sh, err = c.NNSContractAddress(name)
return err
})
}
cs, err := c.client.GetContractStateByID(nnsContractID) // cache it?
if err != nil {
return sh, fmt.Errorf("NNS contract state: %w", err)
}
sh, err = nnsResolve(c.client, cs.Hash, name)
if err != nil {
return sh, fmt.Errorf("NNS.resolve: %w", err)
}
return sh, nil
}
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,
Value: domain,
},
{
Type: smartcontract.IntegerType,
Value: int64(nns.TXT),
},
}, nil)
if err != nil {
return util.Uint160{}, err
}
if result.State != vm.HaltState.String() {
return util.Uint160{}, fmt.Errorf("invocation failed: %s", result.FaultException)
}
if len(result.Stack) == 0 {
return util.Uint160{}, errEmptyResultStack
}
// Parse the result of resolving NNS record.
// It works with multiple formats (corresponding to multiple NNS versions).
// If array of hashes is provided, it returns only the first one.
res := result.Stack[0]
if arr, ok := res.Value().([]stackitem.Item); ok {
if len(arr) == 0 {
return util.Uint160{}, errors.New("NNS record is missing")
}
res = arr[0]
}
bs, err := res.TryBytes()
if err != nil {
return util.Uint160{}, fmt.Errorf("malformed response: %w", err)
}
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
}