Skip to content

Commit

Permalink
rpc/nns: add a method to resolve NeoFS contracts
Browse files Browse the repository at this point in the history
Fix #348.

Signed-off-by: Roman Khimov <[email protected]>
  • Loading branch information
roman-khimov committed Sep 25, 2023
1 parent ead9189 commit 52a55f5
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
33 changes: 33 additions & 0 deletions rpc/nns/hashes.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package nns

import (
"errors"

"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
)

Expand All @@ -10,6 +13,11 @@ import (
// an ID of 1.
const ID = 1

// ContractTLD is the default TLD for NeoFS contracts. It's a convention that
// is not likely to be used by any non-NeoFS networks, but for NeoFS ones it
// allows to find contract hashes more easily.
const ContractTLD = "neofs"

// ContractStateGetter is the interface required for contract state resolution
// using a known contract ID.
type ContractStateGetter interface {
Expand All @@ -27,3 +35,28 @@ func InferHash(sg ContractStateGetter) (util.Uint160, error) {

return c.Hash, nil
}

// ResolveFSContract is a convenience method that doesn't exist in the NNS
// contract itself (it doesn't care which data is stored there). It assumes
// that contracts follow the [ContractTLD] convention, gets simple contract
// names (like "container" or "netmap") and extracts the hash for the
// respective NNS record in any of the formats (of which historically there's
// been a few).
func (c *ContractReader) ResolveFSContract(name string) (util.Uint160, error) {
strs, err := c.Resolve(name+"."+ContractTLD, TXT)
if err != nil {
return util.Uint160{}, err
}
for i := range strs {
h, err := util.Uint160DecodeStringLE(strs[i])
if err == nil {
return h, nil
}

h, err = address.StringToUint160(strs[i])
if err == nil {
return h, nil
}
}
return util.Uint160{}, errors.New("no valid hashes are found")
}
78 changes: 78 additions & 0 deletions rpc/nns/hashes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import (
"errors"
"testing"

"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -35,3 +39,77 @@ func TestInferHash(t *testing.T) {
require.NoError(t, err)
require.Equal(t, util.Uint160{0x01, 0x02, 0x03}, h)
}

type testInv struct {
err error
res *result.Invoke
}

func (t *testInv) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) {
return t.res, t.err
}

func (t *testInv) CallAndExpandIterator(contract util.Uint160, operation string, i int, params ...any) (*result.Invoke, error) {
return t.res, t.err
}
func (t *testInv) TraverseIterator(uuid.UUID, *result.Iterator, int) ([]stackitem.Item, error) {
return nil, nil
}
func (t *testInv) TerminateSession(uuid.UUID) error {
return nil
}

func TestBaseErrors(t *testing.T) {
ti := new(testInv)
r := NewReader(ti, util.Uint160{1, 2, 3})

ti.err = errors.New("bad")
_, err := r.ResolveFSContract("blah")
require.Error(t, err)

ti.err = nil
ti.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make([]stackitem.Item{}),
},
}
_, err = r.ResolveFSContract("blah")
require.Error(t, err)

ti.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make([]stackitem.Item{
stackitem.Make(100500),
}),
},
}
_, err = r.ResolveFSContract("blah")
require.Error(t, err)

h := util.Uint160{1, 2, 3, 4, 5}
ti.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make([]stackitem.Item{
stackitem.Make(h.StringLE()),
}),
},
}
res, err := r.ResolveFSContract("blah")
require.NoError(t, err)
require.Equal(t, h, res)

ti.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make([]stackitem.Item{
stackitem.Make(address.Uint160ToString(h)),
}),
},
}
res, err = r.ResolveFSContract("blah")
require.NoError(t, err)
require.Equal(t, h, res)
}

0 comments on commit 52a55f5

Please sign in to comment.