forked from TrueCloudLab/frostfs-sdk-go
[#327] nns: Simplify code using neo-go v0.99.2
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
parent
2e5c66934c
commit
01c238ddc0
2 changed files with 38 additions and 101 deletions
108
ns/nns.go
108
ns/nns.go
|
@ -2,18 +2,15 @@ package ns
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/url"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/nspcc-dev/neofs-contract/nns"
|
||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||
)
|
||||
|
@ -25,31 +22,9 @@ import (
|
|||
type NNS struct {
|
||||
nnsContract util.Uint160
|
||||
|
||||
neoClient neoClient
|
||||
}
|
||||
|
||||
// represents virtual connection to Neo network used by NNS.Dial.
|
||||
type neoClient interface {
|
||||
// calls specified method of the Neo smart contract with provided parameters.
|
||||
call(contract util.Uint160, method string, prm []smartcontract.Parameter) (*result.Invoke, error)
|
||||
}
|
||||
|
||||
// implements neoClient using Neo HTTP client.
|
||||
//
|
||||
// note: see NNS.Dial to realize why this isn't defined as type wrapper like neoWebSocket.
|
||||
type neoHTTP struct {
|
||||
*rpcclient.Client
|
||||
}
|
||||
|
||||
func (x *neoHTTP) call(contract util.Uint160, method string, prm []smartcontract.Parameter) (*result.Invoke, error) {
|
||||
return x.Client.InvokeFunction(contract, method, prm, nil)
|
||||
}
|
||||
|
||||
// implements neoClient using Neo WebSocket client.
|
||||
type neoWebSocket rpcclient.WSClient
|
||||
|
||||
func (x *neoWebSocket) call(contract util.Uint160, method string, prm []smartcontract.Parameter) (*result.Invoke, error) {
|
||||
return (*rpcclient.WSClient)(x).InvokeFunction(contract, method, prm, nil)
|
||||
invoker interface {
|
||||
Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
|
||||
}
|
||||
}
|
||||
|
||||
// Dial connects to the address of the NNS server. If fails, the instance
|
||||
|
@ -58,39 +33,30 @@ func (x *neoWebSocket) call(contract util.Uint160, method string, prm []smartcon
|
|||
// If URL address scheme is 'ws' or 'wss', then WebSocket protocol is used,
|
||||
// otherwise HTTP.
|
||||
func (n *NNS) Dial(address string) error {
|
||||
// multiSchemeClient unites neoClient and common interface of
|
||||
// neoclient.Client and neoclient.WSClient. Interface is anonymous
|
||||
// multiSchemeClient unites invoker.RPCInvoke and common interface of
|
||||
// rpcclient.Client and rpcclient.WSClient. Interface is anonymous
|
||||
// according to assumption that common interface of these client types
|
||||
// is not required by design and may diverge with changes.
|
||||
var multiSchemeClient interface {
|
||||
neoClient
|
||||
invoker.RPCInvoke
|
||||
// Init turns client to "ready-to-work" state.
|
||||
Init() error
|
||||
// GetContractStateByID returns state of the NNS contract on 1 input.
|
||||
GetContractStateByID(int32) (*state.Contract, error)
|
||||
}
|
||||
var err error
|
||||
|
||||
uri, err := url.Parse(address)
|
||||
if err == nil && (uri.Scheme == "ws" || uri.Scheme == "wss") {
|
||||
cWebSocket, err := rpcclient.NewWS(context.Background(), address, rpcclient.Options{})
|
||||
multiSchemeClient, err = rpcclient.NewWS(context.Background(), address, rpcclient.Options{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("create Neo WebSocket client: %w", err)
|
||||
}
|
||||
|
||||
multiSchemeClient = (*neoWebSocket)(cWebSocket)
|
||||
} else {
|
||||
cHTTP, err := rpcclient.New(context.Background(), address, rpcclient.Options{})
|
||||
multiSchemeClient, err = rpcclient.New(context.Background(), address, rpcclient.Options{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("create Neo HTTP client: %w", err)
|
||||
}
|
||||
|
||||
// if neoHTTP is defined as type wrapper
|
||||
// type neoHTTP neoclient.Client
|
||||
// then next assignment causes compilation error
|
||||
// multiSchemeClient = (*neoHTTP)(cHTTP)
|
||||
multiSchemeClient = &neoHTTP{
|
||||
Client: cHTTP,
|
||||
}
|
||||
}
|
||||
|
||||
if err = multiSchemeClient.Init(); err != nil {
|
||||
|
@ -102,7 +68,7 @@ func (n *NNS) Dial(address string) error {
|
|||
return fmt.Errorf("get NNS contract state: %w", err)
|
||||
}
|
||||
|
||||
n.neoClient = multiSchemeClient
|
||||
n.invoker = invoker.New(multiSchemeClient, nil)
|
||||
n.nnsContract = nnsContract.Hash
|
||||
|
||||
return nil
|
||||
|
@ -116,50 +82,24 @@ func (n *NNS) Dial(address string) error {
|
|||
//
|
||||
// See also https://docs.neo.org/docs/en-us/reference/nns.html.
|
||||
func (n *NNS) ResolveContainerName(name string) (cid.ID, error) {
|
||||
res, err := n.neoClient.call(n.nnsContract, "resolve", []smartcontract.Parameter{
|
||||
{
|
||||
Type: smartcontract.StringType,
|
||||
Value: name + ".container",
|
||||
},
|
||||
{
|
||||
Type: smartcontract.IntegerType,
|
||||
Value: big.NewInt(int64(nns.TXT)),
|
||||
},
|
||||
})
|
||||
arr, err := unwrap.Array(n.invoker.Call(n.nnsContract, "resolve",
|
||||
name+".container", int64(nns.TXT),
|
||||
))
|
||||
if err != nil {
|
||||
return cid.ID{}, fmt.Errorf("invoke NNS contract: %w", err)
|
||||
return cid.ID{}, fmt.Errorf("contract invocation: %w", err)
|
||||
}
|
||||
|
||||
if res.State != vmstate.Halt.String() {
|
||||
return cid.ID{}, fmt.Errorf("NNS contract fault exception: %s", res.FaultException)
|
||||
} else if len(res.Stack) == 0 {
|
||||
return cid.ID{}, errors.New("empty stack in invocation result")
|
||||
}
|
||||
var id cid.ID
|
||||
|
||||
itemArr, err := res.Stack[len(res.Stack)-1].Convert(stackitem.ArrayT) // top stack element is last in the array
|
||||
if err != nil {
|
||||
return cid.ID{}, fmt.Errorf("convert stack item to %s", stackitem.ArrayT)
|
||||
}
|
||||
|
||||
if _, ok := itemArr.(stackitem.Null); !ok {
|
||||
arr, ok := itemArr.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
// unexpected for types from stackitem package
|
||||
return cid.ID{}, errors.New("invalid cast to stack item slice")
|
||||
for i := range arr {
|
||||
bs, err := arr[i].TryBytes()
|
||||
if err != nil {
|
||||
return cid.ID{}, fmt.Errorf("convert array item to byte slice: %w", err)
|
||||
}
|
||||
|
||||
var id cid.ID
|
||||
|
||||
for i := range arr {
|
||||
bs, err := arr[i].TryBytes()
|
||||
if err != nil {
|
||||
return cid.ID{}, fmt.Errorf("convert array item to byte slice: %w", err)
|
||||
}
|
||||
|
||||
err = id.DecodeString(string(bs))
|
||||
if err == nil {
|
||||
return id, nil
|
||||
}
|
||||
err = id.DecodeString(string(bs))
|
||||
if err == nil {
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,9 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/nspcc-dev/neofs-contract/nns"
|
||||
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -30,18 +28,16 @@ type testNeoClient struct {
|
|||
err error
|
||||
}
|
||||
|
||||
func (x *testNeoClient) call(contract util.Uint160, method string, prm []smartcontract.Parameter) (*result.Invoke, error) {
|
||||
require.Equal(x.t, x.expectedContract, contract)
|
||||
require.Equal(x.t, "resolve", method)
|
||||
require.Len(x.t, prm, 2)
|
||||
require.Equal(x.t, smartcontract.StringType, prm[0].Type)
|
||||
require.Equal(x.t, smartcontract.IntegerType, prm[1].Type)
|
||||
require.EqualValues(x.t, big.NewInt(int64(nns.TXT)), prm[1].Value)
|
||||
func (x *testNeoClient) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) {
|
||||
var domain string
|
||||
|
||||
val, ok := prm[0].Value.(string)
|
||||
require.True(x.t, ok)
|
||||
require.True(x.t, strings.HasSuffix(val, ".container"))
|
||||
require.NotEmpty(x.t, strings.TrimSuffix(val, ".container"))
|
||||
require.Equal(x.t, x.expectedContract, contract)
|
||||
require.Equal(x.t, "resolve", operation)
|
||||
require.Len(x.t, params, 2)
|
||||
require.NotPanics(x.t, func() { domain = params[0].(string) })
|
||||
require.NotPanics(x.t, func() { _ = params[1].(int64) })
|
||||
require.True(x.t, strings.HasSuffix(domain, ".container"))
|
||||
require.NotEmpty(x.t, strings.TrimSuffix(domain, ".container"))
|
||||
|
||||
return &x.res, x.err
|
||||
}
|
||||
|
@ -78,14 +74,15 @@ func TestNNS_ResolveContainerName(t *testing.T) {
|
|||
|
||||
n := NNS{
|
||||
nnsContract: nnsContract,
|
||||
neoClient: testC,
|
||||
invoker: testC,
|
||||
}
|
||||
|
||||
t.Run("invocation failure", func(t *testing.T) {
|
||||
testC.err = errors.New("invoke err")
|
||||
err1 := errors.New("invoke err")
|
||||
testC.err = err1
|
||||
|
||||
_, err := n.ResolveContainerName(testContainerName)
|
||||
require.Error(t, err)
|
||||
_, err2 := n.ResolveContainerName(testContainerName)
|
||||
require.ErrorIs(t, err2, err1)
|
||||
})
|
||||
|
||||
testC.err = nil
|
||||
|
|
Loading…
Reference in a new issue