Skip to content

Commit

Permalink
Merge pull request #1857 from nspcc-dev/rpc/nep11
Browse files Browse the repository at this point in the history
rpc: add NEP11 and NNS interfaces to RPC Client
  • Loading branch information
roman-khimov authored Mar 27, 2021
2 parents afb4c1e + ffc2ad3 commit 5b4f6d2
Show file tree
Hide file tree
Showing 16 changed files with 657 additions and 229 deletions.
9 changes: 5 additions & 4 deletions pkg/compiler/native_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/native/nnsrecords"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
Expand Down Expand Up @@ -77,10 +78,10 @@ func TestRoleManagementRole(t *testing.T) {
}

func TestNameServiceRecordType(t *testing.T) {
require.EqualValues(t, native.RecordTypeA, nameservice.TypeA)
require.EqualValues(t, native.RecordTypeCNAME, nameservice.TypeCNAME)
require.EqualValues(t, native.RecordTypeTXT, nameservice.TypeTXT)
require.EqualValues(t, native.RecordTypeAAAA, nameservice.TypeAAAA)
require.EqualValues(t, nnsrecords.A, nameservice.TypeA)
require.EqualValues(t, nnsrecords.CNAME, nameservice.TypeCNAME)
require.EqualValues(t, nnsrecords.TXT, nameservice.TypeTXT)
require.EqualValues(t, nnsrecords.AAAA, nameservice.TypeAAAA)
}

func TestCryptoLibNamedCurve(t *testing.T) {
Expand Down
16 changes: 16 additions & 0 deletions pkg/core/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/native/nnsrecords"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
Expand Down Expand Up @@ -484,6 +485,21 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
require.NoError(t, bc.AddBlock(b))
checkTxHalt(t, bc, txDeploy3.Hash())

// register `neo.com` with A record type and priv0 owner via NNS
transferFundsToCommittee(t, bc) // block #11
res, err := invokeContractMethodGeneric(bc, defaultNameServiceSysfee,
bc.contracts.NameService.Hash, "addRoot", true, "com") // block #12
require.NoError(t, err)
checkResult(t, res, stackitem.Null{})
res, err = invokeContractMethodGeneric(bc, native.DefaultDomainPrice+defaultNameServiceSysfee,
bc.contracts.NameService.Hash, "register", acc0, "neo.com", priv0ScriptHash) // block #13
require.NoError(t, err)
checkResult(t, res, stackitem.NewBool(true))
res, err = invokeContractMethodGeneric(bc, defaultNameServiceSysfee, bc.contracts.NameService.Hash,
"setRecord", acc0, "neo.com", int64(nnsrecords.A), "1.2.3.4") // block #14
require.NoError(t, err)
checkResult(t, res, stackitem.Null{})

// Compile contract to test `invokescript` RPC call
_, _ = newDeployTx(t, bc, priv0ScriptHash, prefix+"invokescript_contract.go", "ContractForInvokescriptTest", nil)
}
Expand Down
40 changes: 15 additions & 25 deletions pkg/core/native/name_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/native/nnsrecords"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
Expand Down Expand Up @@ -42,17 +43,6 @@ type nameState struct {
Admin util.Uint160
}

// RecordType represents name record type.
type RecordType byte

// Pre-defined record types.
const (
RecordTypeA RecordType = 1
RecordTypeCNAME RecordType = 5
RecordTypeTXT RecordType = 16
RecordTypeAAAA RecordType = 28
)

const (
nameServiceID = -10

Expand Down Expand Up @@ -448,19 +438,19 @@ func (n *NameService) setRecord(ic *interop.Context, args []stackitem.Item) stac
return stackitem.Null{}
}

func checkName(rt RecordType, name string) {
func checkName(rt nnsrecords.Type, name string) {
var valid bool
switch rt {
case RecordTypeA:
case nnsrecords.A:
// We can't rely on `len(ip) == net.IPv4len` because
// IPv4 can be parsed to mapped representation.
valid = ipv4Regex.MatchString(name) &&
net.ParseIP(name) != nil
case RecordTypeCNAME:
case nnsrecords.CNAME:
valid = matchName(name)
case RecordTypeTXT:
case nnsrecords.TXT:
valid = utf8.RuneCountInString(name) <= 255
case RecordTypeAAAA:
case nnsrecords.AAAA:
valid = ipv6Regex.MatchString(name) &&
net.ParseIP(name) != nil
}
Expand Down Expand Up @@ -513,34 +503,34 @@ func (n *NameService) resolve(ic *interop.Context, args []stackitem.Item) stacki
return stackitem.NewByteArray([]byte(result))
}

func (n *NameService) resolveInternal(ic *interop.Context, name string, t RecordType, redirect int) (string, bool) {
func (n *NameService) resolveInternal(ic *interop.Context, name string, t nnsrecords.Type, redirect int) (string, bool) {
if redirect < 0 {
panic("invalid redirect")
}
records := n.getRecordsInternal(ic.DAO, name)
if data, ok := records[t]; ok {
return data, true
}
data, ok := records[RecordTypeCNAME]
data, ok := records[nnsrecords.CNAME]
if !ok {
return "", false
}
return n.resolveInternal(ic, data, t, redirect-1)
}

func (n *NameService) getRecordsInternal(d dao.DAO, name string) map[RecordType]string {
func (n *NameService) getRecordsInternal(d dao.DAO, name string) map[nnsrecords.Type]string {
domain := toDomain(name)
key := makeRecordKey(domain, name, 0)
key = key[:len(key)-1]
res := make(map[RecordType]string)
res := make(map[nnsrecords.Type]string)
d.Seek(n.ID, key, func(k, v []byte) {
rt := RecordType(k[len(k)-1])
rt := nnsrecords.Type(k[len(k)-1])
res[rt] = string(v)
})
return res
}

func makeRecordKey(domain, name string, rt RecordType) []byte {
func makeRecordKey(domain, name string, rt nnsrecords.Type) []byte {
key := make([]byte, 1+util.Uint160Size+util.Uint160Size+1)
key[0] = prefixRecord
i := 1
Expand Down Expand Up @@ -647,7 +637,7 @@ func toDomain(name string) string {
return domain
}

func toRecordType(item stackitem.Item) RecordType {
func toRecordType(item stackitem.Item) nnsrecords.Type {
bi, err := item.TryInteger()
if err != nil || !bi.IsInt64() {
panic("invalid record type")
Expand All @@ -656,8 +646,8 @@ func toRecordType(item stackitem.Item) RecordType {
if val > math.MaxUint8 {
panic("invalid record type")
}
switch rt := RecordType(val); rt {
case RecordTypeA, RecordTypeCNAME, RecordTypeTXT, RecordTypeAAAA:
switch rt := nnsrecords.Type(val); rt {
case nnsrecords.A, nnsrecords.CNAME, nnsrecords.TXT, nnsrecords.AAAA:
return rt
default:
panic("invalid record type")
Expand Down
73 changes: 37 additions & 36 deletions pkg/core/native/name_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package native
import (
"testing"

"github.com/nspcc-dev/neo-go/pkg/core/native/nnsrecords"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -36,45 +37,45 @@ func TestParseDomain(t *testing.T) {
func TestNameService_CheckName(t *testing.T) {
// tests are got from the C# implementation
testCases := []struct {
Type RecordType
Type nnsrecords.Type
Name string
ShouldFail bool
}{
{Type: RecordTypeA, Name: "0.0.0.0"},
{Type: RecordTypeA, Name: "10.10.10.10"},
{Type: RecordTypeA, Name: "255.255.255.255"},
{Type: RecordTypeA, Name: "192.168.1.1"},
{Type: RecordTypeA, Name: "1a", ShouldFail: true},
{Type: RecordTypeA, Name: "256.0.0.0", ShouldFail: true},
{Type: RecordTypeA, Name: "01.01.01.01", ShouldFail: true},
{Type: RecordTypeA, Name: "00.0.0.0", ShouldFail: true},
{Type: RecordTypeA, Name: "0.0.0.-1", ShouldFail: true},
{Type: RecordTypeA, Name: "0.0.0.0.1", ShouldFail: true},
{Type: RecordTypeA, Name: "11111111.11111111.11111111.11111111", ShouldFail: true},
{Type: RecordTypeA, Name: "11111111.11111111.11111111.11111111", ShouldFail: true},
{Type: RecordTypeA, Name: "ff.ff.ff.ff", ShouldFail: true},
{Type: RecordTypeA, Name: "0.0.256", ShouldFail: true},
{Type: RecordTypeA, Name: "0.0.0", ShouldFail: true},
{Type: RecordTypeA, Name: "0.257", ShouldFail: true},
{Type: RecordTypeA, Name: "1.1", ShouldFail: true},
{Type: RecordTypeA, Name: "257", ShouldFail: true},
{Type: RecordTypeA, Name: "1", ShouldFail: true},
{Type: RecordTypeAAAA, Name: "2001:db8::8:800:200c:417a"},
{Type: RecordTypeAAAA, Name: "ff01::101"},
{Type: RecordTypeAAAA, Name: "::1"},
{Type: RecordTypeAAAA, Name: "::"},
{Type: RecordTypeAAAA, Name: "2001:db8:0:0:8:800:200c:417a"},
{Type: RecordTypeAAAA, Name: "ff01:0:0:0:0:0:0:101"},
{Type: RecordTypeAAAA, Name: "0:0:0:0:0:0:0:1"},
{Type: RecordTypeAAAA, Name: "0:0:0:0:0:0:0:0"},
{Type: RecordTypeAAAA, Name: "2001:DB8::8:800:200C:417A", ShouldFail: true},
{Type: RecordTypeAAAA, Name: "FF01::101", ShouldFail: true},
{Type: RecordTypeAAAA, Name: "fF01::101", ShouldFail: true},
{Type: RecordTypeAAAA, Name: "2001:DB8:0:0:8:800:200C:417A", ShouldFail: true},
{Type: RecordTypeAAAA, Name: "FF01:0:0:0:0:0:0:101", ShouldFail: true},
{Type: RecordTypeAAAA, Name: "::ffff:1.01.1.01", ShouldFail: true},
{Type: RecordTypeAAAA, Name: "2001:DB8:0:0:8:800:200C:4Z", ShouldFail: true},
{Type: RecordTypeAAAA, Name: "::13.1.68.3", ShouldFail: true},
{Type: nnsrecords.A, Name: "0.0.0.0"},
{Type: nnsrecords.A, Name: "10.10.10.10"},
{Type: nnsrecords.A, Name: "255.255.255.255"},
{Type: nnsrecords.A, Name: "192.168.1.1"},
{Type: nnsrecords.A, Name: "1a", ShouldFail: true},
{Type: nnsrecords.A, Name: "256.0.0.0", ShouldFail: true},
{Type: nnsrecords.A, Name: "01.01.01.01", ShouldFail: true},
{Type: nnsrecords.A, Name: "00.0.0.0", ShouldFail: true},
{Type: nnsrecords.A, Name: "0.0.0.-1", ShouldFail: true},
{Type: nnsrecords.A, Name: "0.0.0.0.1", ShouldFail: true},
{Type: nnsrecords.A, Name: "11111111.11111111.11111111.11111111", ShouldFail: true},
{Type: nnsrecords.A, Name: "11111111.11111111.11111111.11111111", ShouldFail: true},
{Type: nnsrecords.A, Name: "ff.ff.ff.ff", ShouldFail: true},
{Type: nnsrecords.A, Name: "0.0.256", ShouldFail: true},
{Type: nnsrecords.A, Name: "0.0.0", ShouldFail: true},
{Type: nnsrecords.A, Name: "0.257", ShouldFail: true},
{Type: nnsrecords.A, Name: "1.1", ShouldFail: true},
{Type: nnsrecords.A, Name: "257", ShouldFail: true},
{Type: nnsrecords.A, Name: "1", ShouldFail: true},
{Type: nnsrecords.AAAA, Name: "2001:db8::8:800:200c:417a"},
{Type: nnsrecords.AAAA, Name: "ff01::101"},
{Type: nnsrecords.AAAA, Name: "::1"},
{Type: nnsrecords.AAAA, Name: "::"},
{Type: nnsrecords.AAAA, Name: "2001:db8:0:0:8:800:200c:417a"},
{Type: nnsrecords.AAAA, Name: "ff01:0:0:0:0:0:0:101"},
{Type: nnsrecords.AAAA, Name: "0:0:0:0:0:0:0:1"},
{Type: nnsrecords.AAAA, Name: "0:0:0:0:0:0:0:0"},
{Type: nnsrecords.AAAA, Name: "2001:DB8::8:800:200C:417A", ShouldFail: true},
{Type: nnsrecords.AAAA, Name: "FF01::101", ShouldFail: true},
{Type: nnsrecords.AAAA, Name: "fF01::101", ShouldFail: true},
{Type: nnsrecords.AAAA, Name: "2001:DB8:0:0:8:800:200C:417A", ShouldFail: true},
{Type: nnsrecords.AAAA, Name: "FF01:0:0:0:0:0:0:101", ShouldFail: true},
{Type: nnsrecords.AAAA, Name: "::ffff:1.01.1.01", ShouldFail: true},
{Type: nnsrecords.AAAA, Name: "2001:DB8:0:0:8:800:200C:4Z", ShouldFail: true},
{Type: nnsrecords.AAAA, Name: "::13.1.68.3", ShouldFail: true},
}
for _, testCase := range testCases {
if testCase.ShouldFail {
Expand Down
12 changes: 12 additions & 0 deletions pkg/core/native/nnsrecords/nnsrecords.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package nnsrecords

// Type represents name record type.
type Type byte

// Pre-defined record types.
const (
A Type = 1
CNAME Type = 5
TXT Type = 16
AAAA Type = 28
)
Loading

0 comments on commit 5b4f6d2

Please sign in to comment.