-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simplify resolving NNS hashes via RPC binding (#361)
- Loading branch information
Showing
4 changed files
with
244 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package nns_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
|
||
"github.com/nspcc-dev/neo-go/pkg/rpcclient" | ||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" | ||
"github.com/nspcc-dev/neofs-contract/rpc/nns" | ||
) | ||
|
||
// Resolve addresses of NeoFS smart contracts deployed in a particular | ||
// NeoFS sidechain by their NNS domain names. | ||
func ExampleContractReader_ResolveFSContract() { | ||
const sidechainRPCEndpoint = "https://rpc1.morph.fs.neo.org:40341" | ||
|
||
c, err := rpcclient.New(context.Background(), sidechainRPCEndpoint, rpcclient.Options{}) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
err = c.Init() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
nnsAddress, err := nns.InferHash(c) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
nnsContract := nns.NewReader(invoker.New(c, nil), nnsAddress) | ||
|
||
for _, name := range []string{ | ||
nns.NameAudit, | ||
nns.NameBalance, | ||
nns.NameContainer, | ||
nns.NameNeoFSID, | ||
nns.NameNetmap, | ||
nns.NameProxy, | ||
nns.NameReputation, | ||
} { | ||
addr, err := nnsContract.ResolveFSContract(name) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
fmt.Printf("%s: %s\n", name, addr) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
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" | ||
) | ||
|
||
// ID is the default NNS contract ID in all NeoFS networks. NeoFS networks | ||
// always deploy NNS first and can't work without it, therefore it always gets | ||
// 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 { | ||
GetContractStateByID(int32) (*state.Contract, error) | ||
} | ||
|
||
// InferHash simplifies resolving NNS contract hash in existing NeoFS networks. | ||
// It assumes that NNS follows [ID] assignment assumptions which likely won't | ||
// be the case for any non-NeoFS network. | ||
func InferHash(sg ContractStateGetter) (util.Uint160, error) { | ||
c, err := sg.GetContractStateByID(ID) | ||
if err != nil { | ||
return util.Uint160{}, err | ||
} | ||
|
||
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") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package nns | ||
|
||
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" | ||
) | ||
|
||
type stateGetter struct { | ||
f func(int32) (*state.Contract, error) | ||
} | ||
|
||
func (s stateGetter) GetContractStateByID(id int32) (*state.Contract, error) { | ||
return s.f(id) | ||
} | ||
|
||
func TestInferHash(t *testing.T) { | ||
var sg stateGetter | ||
sg.f = func(int32) (*state.Contract, error) { | ||
return nil, errors.New("bad") | ||
} | ||
_, err := InferHash(sg) | ||
require.Error(t, err) | ||
sg.f = func(int32) (*state.Contract, error) { | ||
return &state.Contract{ | ||
ContractBase: state.ContractBase{ | ||
Hash: util.Uint160{0x01, 0x02, 0x03}, | ||
}, | ||
}, nil | ||
} | ||
h, err := InferHash(sg) | ||
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package nns | ||
|
||
// A set of standard contract names deployed into NeoFS sidechain. | ||
const ( | ||
// NameAlphabetPrefix differs from other names in this list, because | ||
// in reality there will be multiple alphabets contract deployed to | ||
// a network named alphabet0, alphabet1, alphabet2, etc. | ||
NameAlphabetPrefix = "alphabet" | ||
NameAudit = "audit" | ||
NameBalance = "balance" | ||
NameContainer = "container" | ||
NameNeoFSID = "neofsid" | ||
NameNetmap = "netmap" | ||
NameProxy = "proxy" | ||
NameReputation = "reputation" | ||
) |