[#42] add ResolveContractHash method #111
2 changed files with 127 additions and 0 deletions
44
ns/nns.go
44
ns/nns.go
|
@ -10,6 +10,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"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/neorpc/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
|
@ -116,3 +117,46 @@ func (n *NNS) ResolveContainerDomain(domain container.Domain) (cid.ID, error) {
|
||||||
|
|
||||||
return cid.ID{}, errNotFound
|
return cid.ID{}, errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolveContractHash looks up for NNS TXT records for the given container domain
|
||||||
|
// by calling `resolve` method of NNS contract. Returns the first record which represents
|
||||||
|
// valid contract hash 20 bytes long unsigned integer. Otherwise, returns an error.
|
||||||
dkirillov marked this conversation as resolved
Outdated
|
|||||||
|
//
|
||||||
|
// ResolveContractHash MUST NOT be called before successful Dial.
|
||||||
|
//
|
||||||
|
// See also https://docs.neo.org/docs/en-us/reference/nns.html.
|
||||||
|
func (n *NNS) ResolveContractHash(domain container.Domain) (util.Uint160, error) {
|
||||||
dkirillov marked this conversation as resolved
Outdated
dkirillov
commented
I'm not sure if it's a good idea to use I'm not sure if it's a good idea to use `container.Domain` when we resolve contract hash. Maybe it's too confusing and we should provide just string as argument. What do you think?
alexvanin
commented
String should be a valid domain, e.g. String should be a valid domain, e.g. `container.frostfs`. I would rather move `container.Domain` structure from container package to some sort of shared package.
|
|||||||
|
item, err := unwrap.Item(n.invoker.Call(n.nnsContract, "resolve",
|
||||||
|
domain.Name()+"."+domain.Zone(), int64(nns.TXT),
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint160{}, fmt.Errorf("contract invocation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := item.(stackitem.Null); !ok {
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
// unexpected for types from stackitem package
|
||||||
|
return util.Uint160{}, errors.New("invalid cast to stack item slice")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range arr {
|
||||||
|
recordValue, err := arr[i].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint160{}, fmt.Errorf("convert array item to byte slice: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
strRecordValue := string(recordValue)
|
||||||
|
scriptHash, err := address.StringToUint160(strRecordValue)
|
||||||
fyrchik
commented
Can it be false? We check the error 5 lines above. Can it be false? We check the error 5 lines above.
aarifullin
commented
You've already checked that You've already checked that `err != nil` above :)
|
|||||||
|
if err == nil {
|
||||||
|
return scriptHash, nil
|
||||||
|
}
|
||||||
|
scriptHash, err = util.Uint160DecodeStringLE(strRecordValue)
|
||||||
|
if err == nil {
|
||||||
|
return scriptHash, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.Uint160{}, errNotFound
|
||||||
|
}
|
||||||
|
|
|
@ -154,3 +154,86 @@ func TestNNS_ResolveContainerDomain(t *testing.T) {
|
||||||
require.Equal(t, id, res)
|
require.Equal(t, id, res)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNNS_ResolveContractHash(t *testing.T) {
|
||||||
|
var testContainerDomain container.Domain
|
||||||
|
testContainerDomain.SetName("some_container")
|
||||||
|
|
||||||
|
var nnsContract util.Uint160
|
||||||
|
|
||||||
|
rand.Read(nnsContract[:])
|
||||||
|
|
||||||
|
testC := &testNeoClient{
|
||||||
|
t: t,
|
||||||
|
expectedContract: nnsContract,
|
||||||
|
}
|
||||||
|
|
||||||
|
n := NNS{
|
||||||
|
nnsContract: nnsContract,
|
||||||
|
invoker: testC,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("invocation failure", func(t *testing.T) {
|
||||||
|
err1 := errors.New("invoke err")
|
||||||
|
testC.err = err1
|
||||||
|
|
||||||
|
_, err2 := n.ResolveContractHash(testContainerDomain)
|
||||||
|
require.ErrorIs(t, err2, err1)
|
||||||
|
})
|
||||||
|
|
||||||
|
testC.err = nil
|
||||||
|
|
||||||
|
t.Run("fault exception", func(t *testing.T) {
|
||||||
|
_, err := n.ResolveContractHash(testContainerDomain)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
testC.res.State = vmstate.Halt.String()
|
||||||
|
|
||||||
|
t.Run("empty stack", func(t *testing.T) {
|
||||||
|
_, err := n.ResolveContractHash(testContainerDomain)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
testC.res.Stack = make([]stackitem.Item, 1)
|
||||||
|
|
||||||
|
t.Run("non-array last stack item", func(t *testing.T) {
|
||||||
|
testC.res.Stack[0] = stackitem.NewBigInteger(big.NewInt(11))
|
||||||
|
|
||||||
|
_, err := n.ResolveContractHash(testContainerDomain)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("null array", func(t *testing.T) {
|
||||||
|
testC.res.Stack[0] = stackitem.Null{}
|
||||||
|
|
||||||
|
_, err := n.ResolveContractHash(testContainerDomain)
|
||||||
|
require.ErrorIs(t, err, errNotFound)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("array stack item with non-slice value", func(t *testing.T) {
|
||||||
|
testC.res.Stack[0] = brokenArrayStackItem{}
|
||||||
|
|
||||||
|
_, err := n.ResolveContractHash(testContainerDomain)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
arr := make([]stackitem.Item, 2)
|
||||||
|
testC.res.Stack[0] = stackitem.NewArray(arr)
|
||||||
|
|
||||||
|
t.Run("non-bytes array element", func(t *testing.T) {
|
||||||
|
arr[0] = stackitem.NewArray(nil)
|
||||||
|
|
||||||
|
_, err := n.ResolveContractHash(testContainerDomain)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
arr[0] = stackitem.NewByteArray([]byte("some byte array 1"))
|
||||||
|
|
||||||
|
t.Run("non-container array elements", func(t *testing.T) {
|
||||||
|
arr[1] = stackitem.NewByteArray([]byte("some byte array 2"))
|
||||||
|
|
||||||
|
_, err := n.ResolveContractHash(testContainerDomain)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue
It doesn't return
first record which represents valid container ID in a string format