diff --git a/pkg/innerring/nns.go b/pkg/innerring/nns.go index 1cd205c9346..c82fdb50b30 100644 --- a/pkg/innerring/nns.go +++ b/pkg/innerring/nns.go @@ -7,7 +7,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" nnsrpc "github.com/nspcc-dev/neofs-contract/rpc/nns" "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/privatedomains" ) @@ -29,82 +28,6 @@ func newNeoFSNNS(contractAddress util.Uint160, contractCaller nep11.Invoker) *ne var errDomainNotFound = errors.New("domain not found") -type errWrongStackItemType struct { - expected, got stackitem.Type -} - -func wrongStackItemTypeError(expected, got stackitem.Type) errWrongStackItemType { - return errWrongStackItemType{ - expected: expected, - got: got, - } -} - -func (x errWrongStackItemType) Error() string { - return fmt.Sprintf("wrong type: expected %s, got %s", x.expected, x.got) -} - -type errInvalidNumberOfStructFields struct { - expected, got int -} - -func invalidNumberOfStructFieldsError(expected, got int) errInvalidNumberOfStructFields { - return errInvalidNumberOfStructFields{ - expected: expected, - got: got, - } -} - -func (x errInvalidNumberOfStructFields) Error() string { - return fmt.Sprintf("invalid number of struct fields: expected %d, got %d", x.expected, x.got) -} - -type errInvalidStructField struct { - index uint - cause error -} - -func invalidStructFieldError(index uint, cause error) errInvalidStructField { - return errInvalidStructField{ - index: index, - cause: cause, - } -} - -func (x errInvalidStructField) Error() string { - return fmt.Sprintf("invalid struct field #%d: %v", x.index, x.cause) -} - -func (x errInvalidStructField) Unwrap() error { - return x.cause -} - -type errInvalidNNSDomainRecord struct { - domain string - record string - cause error -} - -func invalidNNSDomainRecordError(domain, record string, cause error) errInvalidNNSDomainRecord { - return errInvalidNNSDomainRecord{ - domain: domain, - record: record, - cause: cause, - } -} - -func (x errInvalidNNSDomainRecord) Error() string { - if x.record != "" { - return fmt.Sprintf("invalid record %q of the NNS domain %q: %v", x.record, x.domain, x.cause) - } - - return fmt.Sprintf("invalid record of the NNS domain %q: %v", x.domain, x.cause) -} - -func (x errInvalidNNSDomainRecord) Unwrap() error { - return x.cause -} - // CheckDomainRecord calls iterating 'getAllRecords' method of the parameterized // Neo smart contract passing the given domain name. If contract throws 'token // not found' exception, CheckDomainRecord returns errDomainNotFound. Each value @@ -114,77 +37,21 @@ func (x errInvalidNNSDomainRecord) Unwrap() error { // CheckDomainRecord returns nil. Otherwise, // [privatedomains.ErrMissingDomainRecord] is returned. func (x *neoFSNNS) CheckDomainRecord(domain string, record string) error { - sessionID, iter, err := x.contract.GetAllRecords(domain) + records, err := x.contract.Resolve(domain, nnsrpc.TXT) if err != nil { // Track https://github.com/nspcc-dev/neofs-node/issues/2583. if strings.Contains(err.Error(), "token not found") { return errDomainNotFound } - return fmt.Errorf("get iterator over all records of the NNS domain %q: %w", domain, err) + return fmt.Errorf("get all text records of the NNS domain %q: %w", domain, err) } - defer func() { - _ = x.invoker.TerminateSession(sessionID) - }() - - hasRecords := false - - for { - items, err := x.invoker.TraverseIterator(sessionID, &iter, 10) - if err != nil { - return fmt.Errorf("traverse iterator over all records of the NNS domain %q: %w", domain, err) - } - - if len(items) == 0 { - break - } - - hasRecords = true - - for i := range items { - fields, ok := items[i].Value().([]stackitem.Item) - if !ok { - return invalidNNSDomainRecordError(domain, "", - wrongStackItemTypeError(stackitem.StructT, items[i].Type())) - } - - if len(fields) < 3 { - return invalidNNSDomainRecordError(domain, "", - invalidNumberOfStructFieldsError(3, len(fields))) - } - - _, err = fields[0].TryBytes() - if err != nil { - return invalidNNSDomainRecordError(domain, "", - invalidStructFieldError(0, wrongStackItemTypeError(stackitem.ByteArrayT, fields[0].Type()))) - } - - typ, err := fields[1].TryInteger() - if err != nil { - return invalidNNSDomainRecordError(domain, "", - invalidStructFieldError(1, wrongStackItemTypeError(stackitem.IntegerT, fields[1].Type()))) - } - - if typ.Cmp(nnsrpc.TXT) != 0 { - continue - } - - data, err := fields[2].TryBytes() - if err != nil { - return invalidNNSDomainRecordError(domain, "", - invalidStructFieldError(2, wrongStackItemTypeError(stackitem.ByteArrayT, fields[2].Type()))) - } - - if string(data) == record { - return nil - } + for i := range records { + if records[i] == record { + return nil } } - if hasRecords { - return privatedomains.ErrMissingDomainRecord - } - - return nil + return privatedomains.ErrMissingDomainRecord } diff --git a/pkg/innerring/nns_test.go b/pkg/innerring/nns_test.go index e664c84a204..d5fa15533cf 100644 --- a/pkg/innerring/nns_test.go +++ b/pkg/innerring/nns_test.go @@ -25,11 +25,6 @@ type testInvoker struct { callRes *result.Invoke callErr error - - traverseErr error - - items []stackitem.Item - readItems int } func newTestInvoker(tb testing.TB, contractAddr util.Uint160, domain string) *testInvoker { @@ -42,9 +37,10 @@ func newTestInvoker(tb testing.TB, contractAddr util.Uint160, domain string) *te func (x *testInvoker) Call(contract util.Uint160, method string, args ...any) (*result.Invoke, error) { require.Equal(x.tb, x.contractAddr, contract) - require.Equal(x.tb, "getAllRecords", method) - require.Len(x.tb, args, 1) + require.Equal(x.tb, "resolve", method) + require.Len(x.tb, args, 2) require.Equal(x.tb, x.domain, args[0]) + require.Equal(x.tb, nnsrpc.TXT, args[1]) if x.callErr != nil { return nil, x.callErr @@ -58,26 +54,11 @@ func (x *testInvoker) CallAndExpandIterator(contract util.Uint160, method string } func (x *testInvoker) TerminateSession(sessionID uuid.UUID) error { - require.Equal(x.tb, x.callRes.Session, sessionID) - return nil + panic("not expected to be called") } func (x *testInvoker) TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) { - require.Equal(x.tb, x.callRes.Session, sessionID) - - if x.traverseErr != nil { - return nil, x.traverseErr - } - - if num > len(x.items)-x.readItems { - num = len(x.items) - x.readItems - } - - defer func() { - x.readItems += num - }() - - return x.items[x.readItems : x.readItems+num], nil + panic("not expected to be called") } func TestNeoFSNNS_CheckDomainRecord(t *testing.T) { @@ -125,28 +106,26 @@ func TestNeoFSNNS_CheckDomainRecord(t *testing.T) { nnsService := newNeoFSNNS(contractAddr, inv) err := nnsService.CheckDomainRecord(domain, searchedRecord) - require.Error(t, err) + require.ErrorContains(t, err, "result stack is empty") inv.callRes.Stack = make([]stackitem.Item, 2) err = nnsService.CheckDomainRecord(domain, searchedRecord) require.Error(t, err) - nonIteratorItem := stackitem.NewBool(true) - inv.callRes.Stack = []stackitem.Item{nonIteratorItem} + nonArrayItem := stackitem.NewBool(true) + inv.callRes.Stack = []stackitem.Item{nonArrayItem} err = nnsService.CheckDomainRecord(domain, searchedRecord) - require.Error(t, err) + require.ErrorContains(t, err, "not an array") }) newWithRecords := func(items ...stackitem.Item) *neoFSNNS { - var iter result.Iterator inv := newTestInvoker(t, contractAddr, domain) inv.callRes = &result.Invoke{ State: vmstate.Halt.String(), - Stack: []stackitem.Item{stackitem.NewInterop(iter)}, + Stack: []stackitem.Item{stackitem.NewArray(items)}, } - inv.items = items return newNeoFSNNS(contractAddr, inv) } @@ -155,129 +134,22 @@ func TestNeoFSNNS_CheckDomainRecord(t *testing.T) { nnsService := newWithRecords() err := nnsService.CheckDomainRecord(domain, searchedRecord) - require.NoError(t, err) + require.ErrorIs(t, err, privatedomains.ErrMissingDomainRecord) }) - checkInvalidRecordError := func(record string, err error) { - var e errInvalidNNSDomainRecord - require.ErrorAs(t, err, &e) - require.Equal(t, domain, e.domain) - require.Equal(t, record, e.record) - } - - checkInvalidStructFieldError := func(index uint, err error) { - var e errInvalidStructField - require.ErrorAs(t, err, &e) - require.Equal(t, index, e.index) - } - t.Run("with invalid record", func(t *testing.T) { - t.Run("non-struct element", func(t *testing.T) { - nnsService := newWithRecords(stackitem.NewBool(true)) - - err := nnsService.CheckDomainRecord(domain, searchedRecord) - - checkInvalidRecordError("", err) - - var wrongTypeErr errWrongStackItemType - require.ErrorAs(t, err, &wrongTypeErr) - require.Equal(t, stackitem.StructT, wrongTypeErr.expected) - require.Equal(t, stackitem.BooleanT, wrongTypeErr.got) - }) - - t.Run("invalid number of fields", func(t *testing.T) { - for i := 0; i < 3; i++ { - nnsService := newWithRecords(stackitem.NewStruct(make([]stackitem.Item, i))) - - err := nnsService.CheckDomainRecord(domain, searchedRecord) - - checkInvalidRecordError("", err) - - var invalidFieldNumErr errInvalidNumberOfStructFields - require.ErrorAs(t, err, &invalidFieldNumErr) - require.Equal(t, 3, invalidFieldNumErr.expected, i) - require.Equal(t, i, invalidFieldNumErr.got, i) - } - }) - - t.Run("invalid 1st field", func(t *testing.T) { - nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ - stackitem.NewMap(), - stackitem.NewBigInteger(nnsrpc.TXT), - stackitem.NewByteArray([]byte("any")), - })) + t.Run("non-string element", func(t *testing.T) { + nnsService := newWithRecords(stackitem.NewMap()) err := nnsService.CheckDomainRecord(domain, searchedRecord) - - checkInvalidRecordError("", err) - checkInvalidStructFieldError(0, err) - - var wrongTypeErr errWrongStackItemType - require.ErrorAs(t, err, &wrongTypeErr) - require.Equal(t, stackitem.ByteArrayT, wrongTypeErr.expected) - require.Equal(t, stackitem.MapT, wrongTypeErr.got) - }) - - t.Run("invalid 2nd field", func(t *testing.T) { - t.Run("non-integer", func(t *testing.T) { - nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewMap(), - stackitem.NewByteArray([]byte("any")), - })) - - err := nnsService.CheckDomainRecord(domain, searchedRecord) - - checkInvalidRecordError("", err) - checkInvalidStructFieldError(1, err) - - var wrongTypeErr errWrongStackItemType - require.ErrorAs(t, err, &wrongTypeErr) - require.Equal(t, stackitem.IntegerT, wrongTypeErr.expected) - require.Equal(t, stackitem.MapT, wrongTypeErr.got) - }) - - t.Run("non-TXT record", func(t *testing.T) { - nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewBigInteger(nnsrpc.CNAME), - stackitem.NewByteArray([]byte("any")), - })) - - err := nnsService.CheckDomainRecord(domain, searchedRecord) - require.ErrorIs(t, err, privatedomains.ErrMissingDomainRecord) - }) - }) - - t.Run("invalid 3rd field", func(t *testing.T) { - t.Run("invalid type", func(t *testing.T) { - nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewBigInteger(nnsrpc.TXT), - stackitem.NewMap(), - })) - - err := nnsService.CheckDomainRecord(domain, searchedRecord) - - checkInvalidRecordError("", err) - checkInvalidStructFieldError(2, err) - - var wrongTypeErr errWrongStackItemType - require.ErrorAs(t, err, &wrongTypeErr) - require.Equal(t, stackitem.ByteArrayT, wrongTypeErr.expected) - require.Equal(t, stackitem.MapT, wrongTypeErr.got) - }) + require.ErrorIs(t, err, stackitem.ErrInvalidConversion) }) }) t.Run("without searched record", func(t *testing.T) { records := make([]stackitem.Item, 100) for i := range records { - records[i] = stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewBigInteger(nnsrpc.TXT), - stackitem.NewByteArray([]byte(searchedRecord + hex.EncodeToString([]byte{byte(i)}))), - }) + records[i] = stackitem.NewByteArray([]byte(searchedRecord + hex.EncodeToString([]byte{byte(i)}))) } nnsService := newWithRecords(records...) @@ -289,18 +161,10 @@ func TestNeoFSNNS_CheckDomainRecord(t *testing.T) { t.Run("with searched record", func(t *testing.T) { records := make([]stackitem.Item, 100) for i := range records { - records[i] = stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewBigInteger(nnsrpc.TXT), - stackitem.NewByteArray([]byte(searchedRecord + hex.EncodeToString([]byte{byte(i)}))), - }) + records[i] = stackitem.NewByteArray([]byte(searchedRecord + hex.EncodeToString([]byte{byte(i)}))) } - records = append(records, stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewBigInteger(nnsrpc.TXT), - stackitem.NewByteArray([]byte(searchedRecord)), - })) + records = append(records, stackitem.NewByteArray([]byte(searchedRecord))) nnsService := newWithRecords(records...)