Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpc: add NEP11 and NNS interfaces to RPC Client #1857

Merged
merged 8 commits into from
Mar 27, 2021
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