forked from TrueCloudLab/frostfs-contract
Compare commits
1 commit
master
...
feature/77
Author | SHA1 | Date | |
---|---|---|---|
b4ca8cb3f9 |
15 changed files with 394 additions and 794 deletions
|
@ -13,9 +13,9 @@ jobs:
|
|||
- name: Setup Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.20'
|
||||
|
||||
- name: Run commit format checker
|
||||
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
|
||||
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v2
|
||||
with:
|
||||
from: 'origin/${{ github.event.pull_request.base.ref }}'
|
||||
|
|
|
@ -7,7 +7,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go_versions: [ '1.21', '1.22' ]
|
||||
go_versions: [ '1.19', '1.20' ]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
|
|
@ -32,7 +32,7 @@ func CheckVersion(from int) {
|
|||
if from < PrevVersion {
|
||||
panic(ErrVersionMismatch + ": expected >=" + std.Itoa(PrevVersion, 10))
|
||||
}
|
||||
if Version <= from {
|
||||
if from == Version {
|
||||
panic(ErrAlreadyUpdated + ": " + std.Itoa(Version, 10))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,10 +29,6 @@ func ReadIteratorItems(inv Invoker, batchSize int, contract util.Uint160, method
|
|||
return arr, nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = inv.TerminateSession(sessionID)
|
||||
}()
|
||||
|
||||
var shouldStop bool
|
||||
res := arr
|
||||
for !shouldStop {
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
)
|
||||
|
||||
|
@ -242,7 +249,7 @@ func (c Client) GetSubject(addr util.Uint160) (*Subject, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseSubject(items)
|
||||
return parseSubject(items)
|
||||
}
|
||||
|
||||
// GetSubjectExtended gets extended subject by address.
|
||||
|
@ -252,12 +259,12 @@ func (c Client) GetSubjectExtended(addr util.Uint160) (*SubjectExtended, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseSubjectExtended(items)
|
||||
return parseSubjectExtended(items)
|
||||
}
|
||||
|
||||
// ListSubjects gets all subjects.
|
||||
func (c Client) ListSubjects() ([]util.Uint160, error) {
|
||||
return UnwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listSubjectsMethod))
|
||||
return unwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listSubjectsMethod))
|
||||
}
|
||||
|
||||
// AddSubjectKey adds extra public key to subject.
|
||||
|
@ -328,7 +335,7 @@ func (c Client) GetSubjectByKey(key *keys.PublicKey) (*Subject, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseSubject(items)
|
||||
return parseSubject(items)
|
||||
}
|
||||
|
||||
// GetSubjectByName gets subject by its name (namespace scope).
|
||||
|
@ -338,7 +345,7 @@ func (c Client) GetSubjectByName(namespace, subjectName string) (*Subject, error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseSubject(items)
|
||||
return parseSubject(items)
|
||||
}
|
||||
|
||||
// GetSubjectKeyByName gets subject public key by its name (namespace scope).
|
||||
|
@ -377,7 +384,7 @@ func (c Client) GetNamespace(namespace string) (*Namespace, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseNamespace(items)
|
||||
return parseNamespace(items)
|
||||
}
|
||||
|
||||
// GetNamespaceExtended gets extended namespace.
|
||||
|
@ -387,7 +394,7 @@ func (c Client) GetNamespaceExtended(namespace string) (*NamespaceExtended, erro
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseNamespaceExtended(items)
|
||||
return parseNamespaceExtended(items)
|
||||
}
|
||||
|
||||
// ListNamespaces gets all namespaces.
|
||||
|
@ -397,12 +404,37 @@ func (c Client) ListNamespaces() ([]*Namespace, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseNamespaces(items)
|
||||
return parseNamespaces(items)
|
||||
}
|
||||
|
||||
func (c Client) ListFullSubjects(hashes []util.Uint160) ([]*Subject, error) {
|
||||
w := io.NewBufBinWriter()
|
||||
|
||||
for _, hash := range hashes {
|
||||
emit.AppCall(w.BinWriter, c.contract, getSubjectMethod, callflag.All, hash)
|
||||
}
|
||||
|
||||
inv, err := c.act.Run(w.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subjects := make([]*Subject, 0)
|
||||
|
||||
for i := range inv.Stack {
|
||||
val := inv.Stack[i].Value().([]stackitem.Item)
|
||||
subject, err := parseSubject(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subjects = append(subjects, subject)
|
||||
}
|
||||
|
||||
return subjects, nil
|
||||
}
|
||||
|
||||
// ListNamespaceSubjects gets all subjects from namespace.
|
||||
func (c Client) ListNamespaceSubjects(namespace string) ([]util.Uint160, error) {
|
||||
return UnwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listNamespaceSubjectsMethod, namespace))
|
||||
return unwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listNamespaceSubjectsMethod, namespace))
|
||||
}
|
||||
|
||||
// CreateGroup creates a new group in specific namespace.
|
||||
|
@ -424,7 +456,7 @@ func (c Client) GetGroup(namespace string, groupID int64) (*Group, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseGroup(items)
|
||||
return parseGroup(items)
|
||||
}
|
||||
|
||||
// GetGroupExtended gets extended group.
|
||||
|
@ -434,7 +466,7 @@ func (c Client) GetGroupExtended(namespace string, groupID int64) (*GroupExtende
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseGroupExtended(items)
|
||||
return parseGroupExtended(items)
|
||||
}
|
||||
|
||||
// SetGroupName updates subject name.
|
||||
|
@ -486,7 +518,7 @@ func (c Client) GetGroupByName(namespace, groupName string) (*Group, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseGroup(items)
|
||||
return parseGroup(items)
|
||||
}
|
||||
|
||||
// ListGroups gets all groups in specific namespace.
|
||||
|
@ -496,7 +528,7 @@ func (c Client) ListGroups(namespace string) ([]*Group, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return ParseGroups(items)
|
||||
return parseGroups(items)
|
||||
}
|
||||
|
||||
// AddSubjectToGroup adds a new subject to group.
|
||||
|
@ -525,7 +557,7 @@ func (c Client) RemoveSubjectFromGroupCall(addr util.Uint160, groupID int64) (me
|
|||
|
||||
// ListGroupSubjects gets all subjects in specific group.
|
||||
func (c Client) ListGroupSubjects(namespace string, groupID int64) ([]util.Uint160, error) {
|
||||
return UnwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listGroupSubjectsMethod, namespace, groupID))
|
||||
return unwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listGroupSubjectsMethod, namespace, groupID))
|
||||
}
|
||||
|
||||
// DeleteGroup deletes group.
|
||||
|
@ -608,3 +640,302 @@ func (c Client) ListNonEmptyGroups(namespace string) ([]string, error) {
|
|||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func unwrapArrayOfUint160(items []stackitem.Item, err error) ([]util.Uint160, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return unwrap.ArrayOfUint160(makeValidRes(stackitem.NewArray(items)))
|
||||
}
|
||||
|
||||
func makeValidRes(item stackitem.Item) (*result.Invoke, error) {
|
||||
return &result.Invoke{
|
||||
Stack: []stackitem.Item{item},
|
||||
State: vmstate.Halt.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeResFromAppExec(res *state.AppExecResult) (*result.Invoke, error) {
|
||||
return &result.Invoke{
|
||||
Stack: res.Stack,
|
||||
State: res.VMState.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseSubject(structArr []stackitem.Item) (*Subject, error) {
|
||||
if len(structArr) < 5 {
|
||||
return nil, errors.New("invalid response subject struct")
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
subj Subject
|
||||
)
|
||||
|
||||
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !structArr[1].Equals(stackitem.Null{}) {
|
||||
subj.AdditionalKeys, err = unwrap.ArrayOfPublicKeys(makeValidRes(structArr[1]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !structArr[2].Equals(stackitem.Null{}) {
|
||||
subj.Namespace, err = unwrap.UTF8String(makeValidRes(structArr[2]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !structArr[2].Equals(stackitem.Null{}) {
|
||||
subj.Name, err = unwrap.UTF8String(makeValidRes(structArr[3]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
subj.KV, err = parseMap(structArr[4])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &subj, nil
|
||||
}
|
||||
|
||||
func parseSubjectExtended(structArr []stackitem.Item) (*SubjectExtended, error) {
|
||||
if len(structArr) < 6 {
|
||||
return nil, errors.New("invalid response subject extended struct")
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
subj SubjectExtended
|
||||
)
|
||||
|
||||
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !structArr[1].Equals(stackitem.Null{}) {
|
||||
subj.AdditionalKeys, err = unwrap.ArrayOfPublicKeys(makeValidRes(structArr[1]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
nsBytes, err := structArr[2].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subj.Namespace = string(nsBytes)
|
||||
|
||||
nameBytes, err := structArr[3].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subj.Name = string(nameBytes)
|
||||
|
||||
subj.KV, err = parseMap(structArr[4])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !structArr[5].Equals(stackitem.Null{}) {
|
||||
groupItems, ok := structArr[5].Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid groups field")
|
||||
}
|
||||
|
||||
subj.Groups, err = parseGroups(groupItems)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &subj, nil
|
||||
}
|
||||
|
||||
func parseMap(item stackitem.Item) (map[string]string, error) {
|
||||
if item.Equals(stackitem.Null{}) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
metaMap, err := unwrap.Map(makeValidRes(item))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
meta, ok := metaMap.Value().([]stackitem.MapElement)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid map type")
|
||||
}
|
||||
|
||||
res := make(map[string]string, len(meta))
|
||||
for _, element := range meta {
|
||||
key, err := element.Key.TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val, err := element.Value.TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res[string(key)] = string(val)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func parseNamespace(structArr []stackitem.Item) (*Namespace, error) {
|
||||
if len(structArr) < 1 {
|
||||
return nil, errors.New("invalid response namespace struct")
|
||||
}
|
||||
|
||||
name, err := structArr[0].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Namespace{Name: string(name)}, nil
|
||||
}
|
||||
|
||||
func parseNamespaceExtended(structArr []stackitem.Item) (*NamespaceExtended, error) {
|
||||
if len(structArr) < 3 {
|
||||
return nil, errors.New("invalid response namespace extended struct")
|
||||
}
|
||||
|
||||
name, err := structArr[0].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groupCount, err := structArr[1].TryInteger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subjectCount, err := structArr[2].TryInteger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NamespaceExtended{
|
||||
Name: string(name),
|
||||
GroupsCount: groupCount.Int64(),
|
||||
SubjectsCount: subjectCount.Int64(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseNamespaces(items []stackitem.Item) ([]*Namespace, error) {
|
||||
var err error
|
||||
res := make([]*Namespace, len(items))
|
||||
|
||||
for i := 0; i < len(items); i++ {
|
||||
arr, ok := items[i].Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid namespace type")
|
||||
}
|
||||
res[i], err = parseNamespace(arr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func parseGroup(structArr []stackitem.Item) (*Group, error) {
|
||||
if len(structArr) < 4 {
|
||||
return nil, errors.New("invalid response group struct")
|
||||
}
|
||||
|
||||
groupID, err := structArr[0].TryInteger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := structArr[1].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespace, err := structArr[2].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kvs, err := parseMap(structArr[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Group{
|
||||
ID: groupID.Int64(),
|
||||
Name: string(name),
|
||||
Namespace: string(namespace),
|
||||
KV: kvs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseGroupExtended(structArr []stackitem.Item) (*GroupExtended, error) {
|
||||
if len(structArr) < 5 {
|
||||
return nil, errors.New("invalid response group extended struct")
|
||||
}
|
||||
|
||||
groupID, err := structArr[0].TryInteger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := structArr[1].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespace, err := structArr[2].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kvs, err := parseMap(structArr[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subjectCount, err := structArr[4].TryInteger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &GroupExtended{
|
||||
ID: groupID.Int64(),
|
||||
Name: string(name),
|
||||
Namespace: string(namespace),
|
||||
KV: kvs,
|
||||
SubjectsCount: subjectCount.Int64(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseGroups(items []stackitem.Item) ([]*Group, error) {
|
||||
var err error
|
||||
res := make([]*Group, len(items))
|
||||
|
||||
for i := 0; i < len(items); i++ {
|
||||
arr, ok := items[i].Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid group type")
|
||||
}
|
||||
res[i], err = parseGroup(arr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -1,312 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"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/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"
|
||||
)
|
||||
|
||||
func UnwrapArrayOfUint160(items []stackitem.Item, err error) ([]util.Uint160, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return unwrap.ArrayOfUint160(makeValidRes(stackitem.NewArray(items)))
|
||||
}
|
||||
|
||||
func ParseSubject(structArr []stackitem.Item) (*Subject, error) {
|
||||
if len(structArr) < 5 {
|
||||
return nil, errors.New("invalid response subject struct")
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
subj Subject
|
||||
)
|
||||
|
||||
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !structArr[1].Equals(stackitem.Null{}) {
|
||||
subj.AdditionalKeys, err = unwrap.ArrayOfPublicKeys(makeValidRes(structArr[1]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !structArr[2].Equals(stackitem.Null{}) {
|
||||
subj.Namespace, err = unwrap.UTF8String(makeValidRes(structArr[2]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !structArr[2].Equals(stackitem.Null{}) {
|
||||
subj.Name, err = unwrap.UTF8String(makeValidRes(structArr[3]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
subj.KV, err = ParseMap(structArr[4])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &subj, nil
|
||||
}
|
||||
|
||||
func ParseSubjectExtended(structArr []stackitem.Item) (*SubjectExtended, error) {
|
||||
if len(structArr) < 6 {
|
||||
return nil, errors.New("invalid response subject extended struct")
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
subj SubjectExtended
|
||||
)
|
||||
|
||||
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !structArr[1].Equals(stackitem.Null{}) {
|
||||
subj.AdditionalKeys, err = unwrap.ArrayOfPublicKeys(makeValidRes(structArr[1]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
nsBytes, err := structArr[2].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subj.Namespace = string(nsBytes)
|
||||
|
||||
nameBytes, err := structArr[3].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subj.Name = string(nameBytes)
|
||||
|
||||
subj.KV, err = ParseMap(structArr[4])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !structArr[5].Equals(stackitem.Null{}) {
|
||||
groupItems, ok := structArr[5].Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid groups field")
|
||||
}
|
||||
|
||||
subj.Groups, err = ParseGroups(groupItems)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &subj, nil
|
||||
}
|
||||
|
||||
func ParseMap(item stackitem.Item) (map[string]string, error) {
|
||||
if item.Equals(stackitem.Null{}) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
metaMap, err := unwrap.Map(makeValidRes(item))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
meta, ok := metaMap.Value().([]stackitem.MapElement)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid map type")
|
||||
}
|
||||
|
||||
res := make(map[string]string, len(meta))
|
||||
for _, element := range meta {
|
||||
key, err := element.Key.TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val, err := element.Value.TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res[string(key)] = string(val)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func ParseNamespace(structArr []stackitem.Item) (*Namespace, error) {
|
||||
if len(structArr) < 1 {
|
||||
return nil, errors.New("invalid response namespace struct")
|
||||
}
|
||||
|
||||
name, err := structArr[0].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Namespace{Name: string(name)}, nil
|
||||
}
|
||||
|
||||
func ParseNamespaceExtended(structArr []stackitem.Item) (*NamespaceExtended, error) {
|
||||
if len(structArr) < 3 {
|
||||
return nil, errors.New("invalid response namespace extended struct")
|
||||
}
|
||||
|
||||
name, err := structArr[0].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groupCount, err := structArr[1].TryInteger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subjectCount, err := structArr[2].TryInteger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NamespaceExtended{
|
||||
Name: string(name),
|
||||
GroupsCount: groupCount.Int64(),
|
||||
SubjectsCount: subjectCount.Int64(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ParseNamespaces(items []stackitem.Item) ([]*Namespace, error) {
|
||||
var err error
|
||||
res := make([]*Namespace, len(items))
|
||||
|
||||
for i := 0; i < len(items); i++ {
|
||||
arr, ok := items[i].Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid namespace type")
|
||||
}
|
||||
res[i], err = ParseNamespace(arr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func ParseGroup(structArr []stackitem.Item) (*Group, error) {
|
||||
if len(structArr) < 4 {
|
||||
return nil, errors.New("invalid response group struct")
|
||||
}
|
||||
|
||||
groupID, err := structArr[0].TryInteger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := structArr[1].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespace, err := structArr[2].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kvs, err := ParseMap(structArr[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Group{
|
||||
ID: groupID.Int64(),
|
||||
Name: string(name),
|
||||
Namespace: string(namespace),
|
||||
KV: kvs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ParseGroupExtended(structArr []stackitem.Item) (*GroupExtended, error) {
|
||||
if len(structArr) < 5 {
|
||||
return nil, errors.New("invalid response group extended struct")
|
||||
}
|
||||
|
||||
groupID, err := structArr[0].TryInteger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := structArr[1].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespace, err := structArr[2].TryBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kvs, err := ParseMap(structArr[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subjectCount, err := structArr[4].TryInteger()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &GroupExtended{
|
||||
ID: groupID.Int64(),
|
||||
Name: string(name),
|
||||
Namespace: string(namespace),
|
||||
KV: kvs,
|
||||
SubjectsCount: subjectCount.Int64(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ParseGroups(items []stackitem.Item) ([]*Group, error) {
|
||||
var err error
|
||||
res := make([]*Group, len(items))
|
||||
|
||||
for i := 0; i < len(items); i++ {
|
||||
arr, ok := items[i].Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid group type")
|
||||
}
|
||||
res[i], err = ParseGroup(arr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func makeValidRes(item stackitem.Item) (*result.Invoke, error) {
|
||||
return &result.Invoke{
|
||||
Stack: []stackitem.Item{item},
|
||||
State: vmstate.Halt.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeResFromAppExec(res *state.AppExecResult) (*result.Invoke, error) {
|
||||
return &result.Invoke{
|
||||
Stack: res.Stack,
|
||||
State: res.VMState.String(),
|
||||
}, nil
|
||||
}
|
|
@ -14,13 +14,6 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
// Subject represents a subject entity.
|
||||
//
|
||||
// Fields:
|
||||
// - Namespace: a string representing the namespace of the subject.
|
||||
// - Name: a string representing the name of the subject.
|
||||
// The name must match the following regex pattern: ^[\w+=,.@-]{1,64}$
|
||||
// The Subject is stored in the storage as a hash(namespace) + hash(name).
|
||||
Subject struct {
|
||||
PrimaryKey interop.PublicKey
|
||||
AdditionalKeys []interop.PublicKey
|
||||
|
@ -38,15 +31,6 @@ type (
|
|||
Groups []Group
|
||||
}
|
||||
|
||||
// Namespace represents a namespace.
|
||||
//
|
||||
// Fields:
|
||||
// - Name: a string representing the name of the namespace.
|
||||
// The custom name must match the following regex pattern:
|
||||
// (^$)|(^[a-z0-9]{1,2}$)|(^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$)
|
||||
// An empty Name is considered the <root> namespace.
|
||||
//
|
||||
// The Namespace is stored in the storage as a hash(name).
|
||||
Namespace struct {
|
||||
Name string
|
||||
}
|
||||
|
@ -57,18 +41,6 @@ type (
|
|||
SubjectsCount int
|
||||
}
|
||||
|
||||
// Group represents a group entity.
|
||||
//
|
||||
// Fields:
|
||||
// - ID: an integer representing the unique identifier of the group.
|
||||
// The ID is generated upon creation and is the sequential number of the group within the namespace.
|
||||
// It cannot be changed once set.
|
||||
// - Name: a string representing the name of the group.
|
||||
// The name must match the following regex pattern: [\w+=,.@-]{1,128}$
|
||||
// - Namespace: a string representing the namespace of the group.
|
||||
// A group exists only within one namespace.
|
||||
//
|
||||
// The group is stored in the storage as a hash(namespace) + hash(name).
|
||||
Group struct {
|
||||
ID int
|
||||
Name string
|
||||
|
@ -118,7 +90,6 @@ func _deploy(data any, isUpdate bool) {
|
|||
runtime.Log("frostfsid contract initialized")
|
||||
}
|
||||
|
||||
// SetAdmin sets the admin address for the contract.
|
||||
func SetAdmin(addr interop.Hash160) {
|
||||
ctx := storage.GetContext()
|
||||
if !common.HasUpdateAccess() {
|
||||
|
@ -128,7 +99,6 @@ func SetAdmin(addr interop.Hash160) {
|
|||
storage.Put(ctx, adminKey, addr)
|
||||
}
|
||||
|
||||
// ClearAdmin removes the admin address from the contract storage.
|
||||
func ClearAdmin() {
|
||||
ctx := storage.GetContext()
|
||||
if !common.HasUpdateAccess() {
|
||||
|
@ -138,7 +108,6 @@ func ClearAdmin() {
|
|||
storage.Delete(ctx, adminKey)
|
||||
}
|
||||
|
||||
// GetAdmin retrieves the admin address from the contract storage.
|
||||
func GetAdmin() interop.Hash160 {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
return storage.Get(ctx, adminKey).(interop.Hash160)
|
||||
|
@ -160,7 +129,6 @@ func Version() int {
|
|||
return common.Version
|
||||
}
|
||||
|
||||
// CreateSubject creates a new subject in the specified namespace with the provided public key.
|
||||
func CreateSubject(ns string, key interop.PublicKey) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -201,7 +169,6 @@ func CreateSubject(ns string, key interop.PublicKey) {
|
|||
runtime.Notify("CreateSubject", interop.Hash160(addr))
|
||||
}
|
||||
|
||||
// AddSubjectKey adds an additional public key to a subject with the specified address.
|
||||
func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -233,7 +200,6 @@ func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
|||
runtime.Notify("AddSubjectKey", addr, key)
|
||||
}
|
||||
|
||||
// RemoveSubjectKey removes an additional public key from the subject with the specified address.
|
||||
func RemoveSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -272,7 +238,6 @@ func RemoveSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
|||
runtime.Notify("RemoveSubjectKey", addr, key)
|
||||
}
|
||||
|
||||
// SetSubjectName sets a new name for the subject with the specified address.
|
||||
func SetSubjectName(addr interop.Hash160, name string) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -297,7 +262,6 @@ func SetSubjectName(addr interop.Hash160, name string) {
|
|||
runtime.Notify("SetSubjectName", addr, name)
|
||||
}
|
||||
|
||||
// SetSubjectKV sets a key-value pair for the subject with the specified address.
|
||||
func SetSubjectKV(addr interop.Hash160, key, val string) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -322,7 +286,6 @@ func SetSubjectKV(addr interop.Hash160, key, val string) {
|
|||
runtime.Notify("SetSubjectKV", addr, key, val)
|
||||
}
|
||||
|
||||
// DeleteSubjectKV deletes a key-value pair from the subject with the specified address.
|
||||
func DeleteSubjectKV(addr interop.Hash160, key string) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -344,7 +307,6 @@ func DeleteSubjectKV(addr interop.Hash160, key string) {
|
|||
runtime.Notify("DeleteSubjectKV", addr, key)
|
||||
}
|
||||
|
||||
// DeleteSubject deletes the subject with the specified address.
|
||||
func DeleteSubject(addr interop.Hash160) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -371,7 +333,6 @@ func DeleteSubject(addr interop.Hash160) {
|
|||
runtime.Notify("DeleteSubject", addr)
|
||||
}
|
||||
|
||||
// GetSubject retrieves the subject with the specified address.
|
||||
func GetSubject(addr interop.Hash160) Subject {
|
||||
if len(addr) != interop.Hash160Len {
|
||||
panic("incorrect address length")
|
||||
|
@ -387,7 +348,6 @@ func GetSubject(addr interop.Hash160) Subject {
|
|||
return std.Deserialize(data).(Subject)
|
||||
}
|
||||
|
||||
// GetSubjectExtended retrieves the extended information of the subject with the specified address.
|
||||
func GetSubjectExtended(addr interop.Hash160) SubjectExtended {
|
||||
subj := GetSubject(addr)
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
|
@ -419,7 +379,6 @@ func GetSubjectExtended(addr interop.Hash160) SubjectExtended {
|
|||
return subjExt
|
||||
}
|
||||
|
||||
// GetSubjectByKey retrieves the subject associated with the provided public key.
|
||||
func GetSubjectByKey(key interop.PublicKey) Subject {
|
||||
if len(key) != interop.PublicKeyCompressedLen {
|
||||
panic("incorrect key length")
|
||||
|
@ -448,14 +407,12 @@ func GetSubjectByKey(key interop.PublicKey) Subject {
|
|||
panic("subject not found")
|
||||
}
|
||||
|
||||
// GetSubjectByName retrieves the subject with the specified name within the given namespace.
|
||||
func GetSubjectByName(ns, name string) Subject {
|
||||
key := GetSubjectKeyByName(ns, name)
|
||||
addr := contract.CreateStandardAccount(key)
|
||||
return GetSubject(addr)
|
||||
}
|
||||
|
||||
// GetSubjectKeyByName retrieves the public key of the subject with the specified namespace and name.
|
||||
func GetSubjectKeyByName(ns, name string) interop.PublicKey {
|
||||
if name == "" {
|
||||
panic("invalid or name")
|
||||
|
@ -477,7 +434,6 @@ func ListSubjects() iterator.Iterator {
|
|||
return storage.Find(ctx, []byte{subjectKeysPrefix}, storage.KeysOnly|storage.RemovePrefix)
|
||||
}
|
||||
|
||||
// CreateNamespace creates a new namespace with the specified name.
|
||||
func CreateNamespace(ns string) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -496,7 +452,6 @@ func CreateNamespace(ns string) {
|
|||
runtime.Notify("CreateNamespace", ns)
|
||||
}
|
||||
|
||||
// GetNamespace retrieves the namespace with the specified name.
|
||||
func GetNamespace(ns string) Namespace {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
nsKey := namespaceKey(ns)
|
||||
|
@ -508,7 +463,6 @@ func GetNamespace(ns string) Namespace {
|
|||
return std.Deserialize(data).(Namespace)
|
||||
}
|
||||
|
||||
// GetNamespaceExtended retrieves extended information about the namespace.
|
||||
func GetNamespaceExtended(ns string) NamespaceExtended {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
nsKey := namespaceKey(ns)
|
||||
|
@ -545,7 +499,6 @@ func ListNamespaceSubjects(ns string) iterator.Iterator {
|
|||
return storage.Find(ctx, namespaceSubjectPrefix(ns), storage.KeysOnly|storage.RemovePrefix)
|
||||
}
|
||||
|
||||
// CreateGroup creates a new group within the specified namespace.
|
||||
func CreateGroup(ns, group string) int {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -580,7 +533,6 @@ func CreateGroup(ns, group string) int {
|
|||
return groupCountID
|
||||
}
|
||||
|
||||
// GetGroup retrieves the group with the specified ID within the given namespace.
|
||||
func GetGroup(ns string, groupID int) Group {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
gKey := groupKey(ns, groupID)
|
||||
|
@ -592,7 +544,6 @@ func GetGroup(ns string, groupID int) Group {
|
|||
return std.Deserialize(data).(Group)
|
||||
}
|
||||
|
||||
// GetGroupExtended retrieves extended information about the group, including the count of subjects in the group.
|
||||
func GetGroupExtended(ns string, groupID int) GroupExtended {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
gKey := groupKey(ns, groupID)
|
||||
|
@ -618,7 +569,6 @@ func GetGroupExtended(ns string, groupID int) GroupExtended {
|
|||
return grExtended
|
||||
}
|
||||
|
||||
// GetGroupIDByName retrieves the ID of the group with the specified name within the given namespace.
|
||||
func GetGroupIDByName(ns, name string) int {
|
||||
if name == "" {
|
||||
panic("invalid name")
|
||||
|
@ -635,7 +585,6 @@ func GetGroupIDByName(ns, name string) int {
|
|||
return std.Deserialize(groupIDRaw).(int)
|
||||
}
|
||||
|
||||
// GetGroupByName retrieves the group with the specified name within the given namespace.
|
||||
func GetGroupByName(ns, name string) Group {
|
||||
groupID := GetGroupIDByName(ns, name)
|
||||
gKey := groupKey(ns, groupID)
|
||||
|
@ -649,7 +598,6 @@ func GetGroupByName(ns, name string) Group {
|
|||
return std.Deserialize(data).(Group)
|
||||
}
|
||||
|
||||
// SetGroupName sets a new name for the group with the specified ID within the given namespace.
|
||||
func SetGroupName(ns string, groupID int, name string) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -670,7 +618,6 @@ func SetGroupName(ns string, groupID int, name string) {
|
|||
runtime.Notify("SetGroupName", ns, groupID, name)
|
||||
}
|
||||
|
||||
// SetGroupKV sets a key-value pair for the group with the specified ID within the given namespace.
|
||||
func SetGroupKV(ns string, groupID int, key, val string) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -691,7 +638,6 @@ func SetGroupKV(ns string, groupID int, key, val string) {
|
|||
runtime.Notify("SetGroupKV", ns, groupID, key, val)
|
||||
}
|
||||
|
||||
// DeleteGroupKV deletes a key-value pair from the group with the specified ID within the given namespace.
|
||||
func DeleteGroupKV(ns string, groupID int, key string) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -714,7 +660,6 @@ func ListGroups(ns string) iterator.Iterator {
|
|||
return storage.Find(ctx, groupPrefix(ns), storage.ValuesOnly|storage.DeserializeValues)
|
||||
}
|
||||
|
||||
// AddSubjectToGroup adds a subject to a group with the specified ID.
|
||||
func AddSubjectToGroup(addr interop.Hash160, groupID int) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -747,7 +692,6 @@ func AddSubjectToGroup(addr interop.Hash160, groupID int) {
|
|||
runtime.Notify("AddSubjectToGroup", addr, subject.Namespace, groupID)
|
||||
}
|
||||
|
||||
// RemoveSubjectFromGroup removes a subject from a group with the specified ID.
|
||||
func RemoveSubjectFromGroup(addr interop.Hash160, groupID int) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
@ -786,7 +730,6 @@ func ListGroupSubjects(ns string, groupID int) iterator.Iterator {
|
|||
return storage.Find(ctx, groupSubjectPrefix(ns, groupID), storage.KeysOnly|storage.RemovePrefix)
|
||||
}
|
||||
|
||||
// DeleteGroup deletes the group with the specified ID within the given namespace.
|
||||
func DeleteGroup(ns string, groupID int) {
|
||||
ctx := storage.GetContext()
|
||||
checkContractOwner(ctx)
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
| 0x20 | int | set of roots |
|
||||
| 0x21 + tokenKey | ByteArray | serialized NameState struct |
|
||||
| 0x22 + tokenKey + Hash160(tokenName) | Hash160 | container contract hash |
|
||||
| 0x23 + tokenKey + Hash160(tokenName) | string | global domain flag |
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
@ -41,9 +41,6 @@ const (
|
|||
// prefixRecord contains map from (token key + hash160(token name) + record type)
|
||||
// to record.
|
||||
prefixRecord byte = 0x22
|
||||
//prefixGlobalDomain contains a flag indicating that this domain was created using GlobalDomain.
|
||||
//This is necessary to distinguish it from regular CNAME records.
|
||||
prefixGlobalDomain byte = 0x23
|
||||
)
|
||||
|
||||
// Values constraints.
|
||||
|
@ -68,14 +65,6 @@ const (
|
|||
defaultRegisterPrice = 10_0000_0000
|
||||
// millisecondsInYear is amount of milliseconds per year.
|
||||
millisecondsInYear = int64(365 * 24 * 3600 * 1000)
|
||||
// errInvalidDomainName is an error message for invalid domain name format.
|
||||
errInvalidDomainName = "invalid domain name format"
|
||||
)
|
||||
|
||||
const (
|
||||
// Cnametgt is a special TXT record ensuring all created subdomains point to the global domain - the value of this variable.
|
||||
//It is guaranteed that two domains cannot point to the same global domain.
|
||||
Cnametgt = "cnametgt"
|
||||
)
|
||||
|
||||
// RecordState is a type that registered entities are saved to.
|
||||
|
@ -231,6 +220,9 @@ func GetPrice() int {
|
|||
// IsAvailable checks whether the provided domain name is available.
|
||||
func IsAvailable(name string) bool {
|
||||
fragments := splitAndCheck(name)
|
||||
if fragments == nil {
|
||||
panic("invalid domain name format")
|
||||
}
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
l := len(fragments)
|
||||
if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[l-1])...)) == nil {
|
||||
|
@ -239,81 +231,15 @@ func IsAvailable(name string) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
checkParentExists(ctx, fragments)
|
||||
checkAvailableGlobalDomain(ctx, name)
|
||||
if parentExpired(ctx, fragments) {
|
||||
panic("parent does not exist or is expired")
|
||||
}
|
||||
return storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...)) == nil
|
||||
}
|
||||
|
||||
// checkAvailableGlobalDomain - triggers a panic if the global domain name is occupied.
|
||||
func checkAvailableGlobalDomain(ctx storage.Context, domain string) {
|
||||
globalDomain := getGlobalDomain(ctx, domain)
|
||||
if globalDomain == "" {
|
||||
return
|
||||
}
|
||||
|
||||
nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(globalDomain))...))
|
||||
if nsBytes != nil {
|
||||
panic("global domain is already taken: " + globalDomain + ". Domain: " + domain)
|
||||
}
|
||||
}
|
||||
|
||||
// getGlobalDomain returns the global domain.
|
||||
func getGlobalDomain(ctx storage.Context, domain string) string {
|
||||
index := std.MemorySearch([]byte(domain), []byte("."))
|
||||
|
||||
if index == -1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
name := domain[index+1:]
|
||||
if name == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return extractCnametgt(ctx, name, domain)
|
||||
}
|
||||
|
||||
// extractCnametgt returns the value of the Cnametgt TXT record.
|
||||
func extractCnametgt(ctx storage.Context, name, domain string) string {
|
||||
fragments := splitAndCheck(domain)
|
||||
|
||||
tokenID := []byte(tokenIDFromName(name))
|
||||
records := getRecordsByType(ctx, tokenID, name, TXT)
|
||||
|
||||
if records == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
globalDomain := ""
|
||||
for _, name := range records {
|
||||
fragments := std.StringSplit(name, "=")
|
||||
if len(fragments) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
if fragments[0] == Cnametgt {
|
||||
globalDomain = fragments[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if globalDomain == "" {
|
||||
return ""
|
||||
}
|
||||
return fragments[0] + "." + globalDomain
|
||||
}
|
||||
|
||||
// checkParentExists panics if any domain from fragments doesn't exist or is expired.
|
||||
func checkParentExists(ctx storage.Context, fragments []string) {
|
||||
if dom := parentExpired(ctx, fragments); dom != "" {
|
||||
panic("domain does not exist or is expired: " + dom)
|
||||
}
|
||||
}
|
||||
|
||||
// parentExpired returns domain from fragments that doesn't exist or is expired.
|
||||
// parentExpired returns true if any domain from fragments doesn't exist or is expired.
|
||||
// first denotes the deepest subdomain to check.
|
||||
func parentExpired(ctx storage.Context, fragments []string) string {
|
||||
func parentExpired(ctx storage.Context, fragments []string) bool {
|
||||
now := int64(runtime.GetTime())
|
||||
last := len(fragments) - 1
|
||||
name := fragments[last]
|
||||
|
@ -323,28 +249,26 @@ func parentExpired(ctx storage.Context, fragments []string) string {
|
|||
}
|
||||
nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...))
|
||||
if nsBytes == nil {
|
||||
return name
|
||||
return true
|
||||
}
|
||||
ns := std.Deserialize(nsBytes.([]byte)).(NameState)
|
||||
if now >= ns.Expiration {
|
||||
return name
|
||||
return true
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return false
|
||||
}
|
||||
|
||||
// Register registers a new domain with the specified owner and name if it's available.
|
||||
func Register(name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool {
|
||||
ctx := storage.GetContext()
|
||||
return register(ctx, name, owner, email, refresh, retry, expire, ttl)
|
||||
}
|
||||
|
||||
// Register registers a new domain with the specified owner and name if it's available.
|
||||
func register(ctx storage.Context, name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool {
|
||||
fragments := splitAndCheck(name)
|
||||
if fragments == nil {
|
||||
panic("invalid domain name format")
|
||||
}
|
||||
|
||||
l := len(fragments)
|
||||
tldKey := append([]byte{prefixRoot}, []byte(fragments[l-1])...)
|
||||
|
||||
ctx := storage.GetContext()
|
||||
tldBytes := storage.Get(ctx, tldKey)
|
||||
if l == 1 {
|
||||
checkCommittee()
|
||||
|
@ -356,8 +280,9 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
|
|||
if tldBytes == nil {
|
||||
panic("TLD not found")
|
||||
}
|
||||
checkParentExists(ctx, fragments)
|
||||
|
||||
if parentExpired(ctx, fragments) {
|
||||
panic("one of the parent domains is not registered")
|
||||
}
|
||||
parentKey := getTokenKey([]byte(name[len(fragments[0])+1:]))
|
||||
nsBytes := storage.Get(ctx, append([]byte{prefixName}, parentKey...))
|
||||
ns := std.Deserialize(nsBytes.([]byte)).(NameState)
|
||||
|
@ -401,8 +326,6 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
|
|||
// NNS expiration is in milliseconds
|
||||
Expiration: int64(runtime.GetTime() + expire*1000),
|
||||
}
|
||||
checkAvailableGlobalDomain(ctx, name)
|
||||
|
||||
putNameStateWithKey(ctx, tokenKey, ns)
|
||||
putSoaRecord(ctx, name, email, refresh, retry, expire, ttl)
|
||||
updateBalance(ctx, []byte(name), owner, +1)
|
||||
|
@ -412,7 +335,9 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
|
|||
|
||||
// Renew increases domain expiration date.
|
||||
func Renew(name string) int64 {
|
||||
checkDomainNameLength(name)
|
||||
if len(name) > maxDomainNameLength {
|
||||
panic("invalid domain name format")
|
||||
}
|
||||
runtime.BurnGas(GetPrice())
|
||||
ctx := storage.GetContext()
|
||||
ns := getNameState(ctx, []byte(name))
|
||||
|
@ -424,7 +349,9 @@ func Renew(name string) int64 {
|
|||
|
||||
// UpdateSOA updates soa record.
|
||||
func UpdateSOA(name, email string, refresh, retry, expire, ttl int) {
|
||||
checkDomainNameLength(name)
|
||||
if len(name) > maxDomainNameLength {
|
||||
panic("invalid domain name format")
|
||||
}
|
||||
ctx := storage.GetContext()
|
||||
ns := getNameState(ctx, []byte(name))
|
||||
ns.checkAdmin()
|
||||
|
@ -433,7 +360,9 @@ func UpdateSOA(name, email string, refresh, retry, expire, ttl int) {
|
|||
|
||||
// SetAdmin updates domain admin.
|
||||
func SetAdmin(name string, admin interop.Hash160) {
|
||||
checkDomainNameLength(name)
|
||||
if len(name) > maxDomainNameLength {
|
||||
panic("invalid domain name format")
|
||||
}
|
||||
if admin != nil && !runtime.CheckWitness(admin) {
|
||||
panic("not witnessed by admin")
|
||||
}
|
||||
|
@ -496,26 +425,13 @@ func GetRecords(name string, typ RecordType) []string {
|
|||
|
||||
// DeleteRecords removes domain records with the specified type.
|
||||
func DeleteRecords(name string, typ RecordType) {
|
||||
ctx := storage.GetContext()
|
||||
deleteRecords(ctx, name, typ)
|
||||
}
|
||||
|
||||
// DeleteRecords removes domain records with the specified type.
|
||||
func deleteRecords(ctx storage.Context, name string, typ RecordType) {
|
||||
if typ == SOA {
|
||||
panic("you cannot delete soa record")
|
||||
}
|
||||
tokenID := []byte(tokenIDFromName(name))
|
||||
ctx := storage.GetContext()
|
||||
ns := getNameState(ctx, tokenID)
|
||||
ns.checkAdmin()
|
||||
|
||||
globalDomainStorage := append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...)
|
||||
globalDomainRaw := storage.Get(ctx, globalDomainStorage)
|
||||
globalDomain := globalDomainRaw.(string)
|
||||
if globalDomainRaw != nil && globalDomain != "" {
|
||||
deleteDomain(ctx, globalDomain)
|
||||
}
|
||||
|
||||
recordsKey := getRecordsKeyByType(tokenID, name, typ)
|
||||
records := storage.Find(ctx, recordsKey, storage.KeysOnly)
|
||||
for iterator.Next(records) {
|
||||
|
@ -525,32 +441,6 @@ func deleteRecords(ctx storage.Context, name string, typ RecordType) {
|
|||
updateSoaSerial(ctx, tokenID)
|
||||
}
|
||||
|
||||
// DeleteDomain deletes the domain with the given name.
|
||||
func DeleteDomain(name string) {
|
||||
ctx := storage.GetContext()
|
||||
deleteDomain(ctx, name)
|
||||
}
|
||||
|
||||
func deleteDomain(ctx storage.Context, name string) {
|
||||
nameKey := append([]byte{prefixName}, getTokenKey([]byte(name))...)
|
||||
tldBytes := storage.Get(ctx, nameKey)
|
||||
if tldBytes == nil {
|
||||
return
|
||||
}
|
||||
|
||||
globalDomainRaw := storage.Get(ctx, append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...))
|
||||
globalDomain := globalDomainRaw.(string)
|
||||
if globalDomainRaw != nil && globalDomain != "" {
|
||||
deleteDomain(ctx, globalDomain)
|
||||
}
|
||||
|
||||
deleteRecords(ctx, name, CNAME)
|
||||
deleteRecords(ctx, name, TXT)
|
||||
deleteRecords(ctx, name, A)
|
||||
deleteRecords(ctx, name, AAAA)
|
||||
storage.Delete(ctx, nameKey)
|
||||
}
|
||||
|
||||
// Resolve resolves given name (not more then three redirects are allowed).
|
||||
func Resolve(name string, typ RecordType) []string {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
|
@ -621,7 +511,9 @@ func getNameState(ctx storage.Context, tokenID []byte) NameState {
|
|||
tokenKey := getTokenKey(tokenID)
|
||||
ns := getNameStateWithKey(ctx, tokenKey)
|
||||
fragments := std.StringSplit(string(tokenID), ".")
|
||||
checkParentExists(ctx, fragments)
|
||||
if parentExpired(ctx, fragments) {
|
||||
panic("parent domain has expired")
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
|
@ -691,33 +583,6 @@ func addRecord(ctx storage.Context, tokenId []byte, name string, typ RecordType,
|
|||
}
|
||||
}
|
||||
|
||||
globalDomainKey := append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...)
|
||||
globalDomainStorage := storage.Get(ctx, globalDomainKey)
|
||||
globalDomain := getGlobalDomain(ctx, name)
|
||||
|
||||
if globalDomainStorage == nil && typ == TXT {
|
||||
if globalDomain != "" {
|
||||
checkAvailableGlobalDomain(ctx, name)
|
||||
nsOriginal := getNameState(ctx, []byte(tokenIDFromName(name)))
|
||||
ns := NameState{
|
||||
Name: globalDomain,
|
||||
Owner: nsOriginal.Owner,
|
||||
Expiration: nsOriginal.Expiration,
|
||||
Admin: nsOriginal.Admin,
|
||||
}
|
||||
|
||||
putNameStateWithKey(ctx, getTokenKey([]byte(globalDomain)), ns)
|
||||
storage.Put(ctx, globalDomainKey, globalDomain)
|
||||
|
||||
var oldOwner interop.Hash160
|
||||
updateBalance(ctx, []byte(name), nsOriginal.Owner, +1)
|
||||
postTransfer(oldOwner, nsOriginal.Owner, []byte(name), nil)
|
||||
putCnameRecord(ctx, globalDomain, name)
|
||||
} else {
|
||||
storage.Put(ctx, globalDomainKey, "")
|
||||
}
|
||||
}
|
||||
|
||||
if typ == CNAME && id != 0 {
|
||||
panic("you shouldn't have more than one CNAME record")
|
||||
}
|
||||
|
@ -758,22 +623,6 @@ func putSoaRecord(ctx storage.Context, name, email string, refresh, retry, expir
|
|||
storage.Put(ctx, recordKey, recBytes)
|
||||
}
|
||||
|
||||
// putCnameRecord stores CNAME domain record.
|
||||
func putCnameRecord(ctx storage.Context, name, data string) {
|
||||
var id byte
|
||||
tokenId := []byte(tokenIDFromName(name))
|
||||
recordKey := getIdRecordKey(tokenId, name, CNAME, id)
|
||||
|
||||
rs := RecordState{
|
||||
Name: name,
|
||||
Type: CNAME,
|
||||
ID: id,
|
||||
Data: data,
|
||||
}
|
||||
recBytes := std.Serialize(rs)
|
||||
storage.Put(ctx, recordKey, recBytes)
|
||||
}
|
||||
|
||||
// updateSoaSerial stores soa domain record.
|
||||
func updateSoaSerial(ctx storage.Context, tokenId []byte) {
|
||||
var id byte
|
||||
|
@ -781,7 +630,7 @@ func updateSoaSerial(ctx storage.Context, tokenId []byte) {
|
|||
|
||||
recBytes := storage.Get(ctx, recordKey)
|
||||
if recBytes == nil {
|
||||
return
|
||||
panic("not found soa record")
|
||||
}
|
||||
rec := std.Deserialize(recBytes.([]byte)).(RecordState)
|
||||
|
||||
|
@ -871,28 +720,20 @@ func isAlNum(c uint8) bool {
|
|||
|
||||
// splitAndCheck splits domain name into parts and validates it.
|
||||
func splitAndCheck(name string) []string {
|
||||
checkDomainNameLength(name)
|
||||
l := len(name)
|
||||
if l < minDomainNameLength || maxDomainNameLength < l {
|
||||
return nil
|
||||
}
|
||||
fragments := std.StringSplit(name, ".")
|
||||
l := len(fragments)
|
||||
l = len(fragments)
|
||||
for i := 0; i < l; i++ {
|
||||
if !checkFragment(fragments[i], i == l-1) {
|
||||
panic(errInvalidDomainName + " '" + name + "': invalid fragment '" + fragments[i] + "'")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fragments
|
||||
}
|
||||
|
||||
// checkDomainNameLength panics if domain name length is out of boundaries.
|
||||
func checkDomainNameLength(name string) {
|
||||
l := len(name)
|
||||
if l > maxDomainNameLength {
|
||||
panic(errInvalidDomainName + " '" + name + "': domain name too long: got = " + std.Itoa(l, 10) + ", max = " + std.Itoa(maxDomainNameLength, 10))
|
||||
}
|
||||
if l < minDomainNameLength {
|
||||
panic(errInvalidDomainName + " '" + name + "': domain name too short: got = " + std.Itoa(l, 10) + ", min = " + std.Itoa(minDomainNameLength, 10))
|
||||
}
|
||||
}
|
||||
|
||||
// checkIPv4 checks record on IPv4 compliance.
|
||||
func checkIPv4(data string) bool {
|
||||
l := len(data)
|
||||
|
@ -1006,6 +847,9 @@ func checkIPv6(data string) bool {
|
|||
// tokenIDFromName returns token ID (domain.root) from the provided name.
|
||||
func tokenIDFromName(name string) string {
|
||||
fragments := splitAndCheck(name)
|
||||
if fragments == nil {
|
||||
panic("invalid domain name format")
|
||||
}
|
||||
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
sum := 0
|
||||
|
|
|
@ -16,8 +16,6 @@ type Kind byte
|
|||
const (
|
||||
Namespace = 'n'
|
||||
Container = 'c'
|
||||
User = 'u'
|
||||
Group = 'g'
|
||||
IAM = 'i'
|
||||
)
|
||||
|
||||
|
@ -117,7 +115,7 @@ func mapToNumeric(ctx storage.Context, kind Kind, name []byte) (mapped int, mapp
|
|||
mKey := mapKey(kind, name)
|
||||
numericID := storage.Get(ctx, mKey)
|
||||
if numericID == nil {
|
||||
return 0, false
|
||||
return
|
||||
}
|
||||
mapped = numericID.(int)
|
||||
mappingExists = true
|
||||
|
|
|
@ -121,28 +121,6 @@ func (c *Contract) AddRecordUnsigned(name string, typ *big.Int, data string) (*t
|
|||
return c.actor.MakeUnsignedCall(c.hash, "addRecord", nil, name, typ, data)
|
||||
}
|
||||
|
||||
// DeleteDomain creates a transaction invoking `deleteDomain` method of the contract.
|
||||
// This transaction is signed and immediately sent to the network.
|
||||
// The values returned are its hash, ValidUntilBlock value and error if any.
|
||||
func (c *Contract) DeleteDomain(name string) (util.Uint256, uint32, error) {
|
||||
return c.actor.SendCall(c.hash, "deleteDomain", name)
|
||||
}
|
||||
|
||||
// DeleteDomainTransaction creates a transaction invoking `deleteDomain` method of the contract.
|
||||
// This transaction is signed, but not sent to the network, instead it's
|
||||
// returned to the caller.
|
||||
func (c *Contract) DeleteDomainTransaction(name string) (*transaction.Transaction, error) {
|
||||
return c.actor.MakeCall(c.hash, "deleteDomain", name)
|
||||
}
|
||||
|
||||
// DeleteDomainUnsigned creates a transaction invoking `deleteDomain` method of the contract.
|
||||
// This transaction is not signed, it's simply returned to the caller.
|
||||
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
|
||||
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
|
||||
func (c *Contract) DeleteDomainUnsigned(name string) (*transaction.Transaction, error) {
|
||||
return c.actor.MakeUnsignedCall(c.hash, "deleteDomain", nil, name)
|
||||
}
|
||||
|
||||
// DeleteRecords creates a transaction invoking `deleteRecords` method of the contract.
|
||||
// This transaction is signed and immediately sent to the network.
|
||||
// The values returned are its hash, ValidUntilBlock value and error if any.
|
||||
|
|
|
@ -196,25 +196,3 @@ func (c *Contract) SetAdminTransaction(addr util.Uint160) (*transaction.Transact
|
|||
func (c *Contract) SetAdminUnsigned(addr util.Uint160) (*transaction.Transaction, error) {
|
||||
return c.actor.MakeUnsignedCall(c.hash, "setAdmin", nil, addr)
|
||||
}
|
||||
|
||||
// Update creates a transaction invoking `update` method of the contract.
|
||||
// This transaction is signed and immediately sent to the network.
|
||||
// The values returned are its hash, ValidUntilBlock value and error if any.
|
||||
func (c *Contract) Update(script []byte, manifest []byte, data any) (util.Uint256, uint32, error) {
|
||||
return c.actor.SendCall(c.hash, "update", script, manifest, data)
|
||||
}
|
||||
|
||||
// UpdateTransaction creates a transaction invoking `update` method of the contract.
|
||||
// This transaction is signed, but not sent to the network, instead it's
|
||||
// returned to the caller.
|
||||
func (c *Contract) UpdateTransaction(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
|
||||
return c.actor.MakeCall(c.hash, "update", script, manifest, data)
|
||||
}
|
||||
|
||||
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
|
||||
// This transaction is not signed, it's simply returned to the caller.
|
||||
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
|
||||
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
|
||||
func (c *Contract) UpdateUnsigned(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
|
||||
return c.actor.MakeUnsignedCall(c.hash, "update", nil, script, manifest, data)
|
||||
}
|
||||
|
|
|
@ -165,14 +165,6 @@ func checkContainerList(t *testing.T, c *neotest.ContractInvoker, expected [][]b
|
|||
})
|
||||
}
|
||||
|
||||
const (
|
||||
// default SOA record field values
|
||||
defaultRefresh = 3600 // 1 hour
|
||||
defaultRetry = 600 // 10 min
|
||||
defaultExpire = 3600 * 24 * 365 * 10 // 10 years
|
||||
defaultTTL = 3600 // 1 hour
|
||||
)
|
||||
|
||||
func TestContainerPut(t *testing.T) {
|
||||
c, cBal, _ := newContainerInvoker(t)
|
||||
|
||||
|
@ -241,63 +233,6 @@ func TestContainerPut(t *testing.T) {
|
|||
})
|
||||
})
|
||||
|
||||
t.Run("create global domain", func(t *testing.T) {
|
||||
ctrNNS := neotest.CompileFile(t, c.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
|
||||
nnsHash := ctrNNS.Hash
|
||||
cNNS := c.CommitteeInvoker(nnsHash)
|
||||
cNNS.Invoke(t, true, "register",
|
||||
"animals", c.CommitteeHash,
|
||||
"whateveriwant@world.com", int64(defaultRefresh), int64(defaultRetry), int64(defaultExpire), int64(defaultTTL))
|
||||
|
||||
cNNS.Invoke(t, true, "register",
|
||||
"ns", c.CommitteeHash,
|
||||
"whateveriwant@world.com", int64(defaultRefresh), int64(defaultRetry), int64(defaultExpire), int64(0))
|
||||
|
||||
cNNS.Invoke(t, true, "register",
|
||||
"poland.ns", c.CommitteeHash,
|
||||
"whateveriwant@world.com", int64(defaultRefresh), int64(defaultRetry), int64(defaultExpire), int64(0))
|
||||
|
||||
cNNS.Invoke(t, true, "register",
|
||||
"sweden.ns", c.CommitteeHash,
|
||||
"whateveriwant@world.com", int64(defaultRefresh), int64(defaultRetry), int64(defaultExpire), int64(defaultExpire))
|
||||
|
||||
cNNS.Invoke(t, stackitem.Null{}, "addRecord",
|
||||
"poland.ns", int64(nns.TXT), nns.Cnametgt+"=animals")
|
||||
|
||||
cNNS.Invoke(t, stackitem.Null{}, "addRecord", "poland.ns", int64(nns.TXT), "random-record")
|
||||
|
||||
cNNS.Invoke(t, stackitem.Null{}, "addRecord", "poland.ns", int64(nns.TXT), "ne-qqq=random-record2")
|
||||
cNNS.Invoke(t, stackitem.Null{}, "addRecord", "sweden.ns", int64(nns.TXT), nns.Cnametgt+"=animals")
|
||||
|
||||
balanceMint(t, cBal, acc, (containerFee+containerAliasFee)*5, []byte{})
|
||||
cNNS.Invoke(t, true, "isAvailable", "bober.animals")
|
||||
putArgs := []any{cnt.value, cnt.sig, cnt.pub, cnt.token, "bober", "poland.ns"}
|
||||
c3 := c.WithSigners(c.Committee, acc)
|
||||
c3.Invoke(t, stackitem.Null{}, "putNamed", putArgs...)
|
||||
|
||||
cNNS.Invoke(t, false, "isAvailable", "bober.animals")
|
||||
|
||||
putArgs = []any{cnt.value, cnt.sig, cnt.pub, cnt.token, "bober", "sweden.ns"}
|
||||
|
||||
c3.InvokeFail(t, "global domain is already taken", "putNamed", putArgs...)
|
||||
cNNS.InvokeFail(t, "global domain is already taken", "isAvailable", "bober.poland.ns")
|
||||
|
||||
cnt2 := dummyContainer(acc)
|
||||
cNNS.Invoke(t, true, "isAvailable", "uzik.poland.ns")
|
||||
putArgs = []any{cnt2.value, cnt2.sig, cnt2.pub, cnt2.token, "uzik", "poland.ns"}
|
||||
c3.Invoke(t, stackitem.Null{}, "putNamed", putArgs...)
|
||||
|
||||
c3.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
|
||||
cNNS.Invoke(t, true, "isAvailable", "bober.animals")
|
||||
cNNS.Invoke(t, false, "isAvailable", "bober.poland.ns")
|
||||
|
||||
cNNS.InvokeFail(t, "global domain is already taken", "isAvailable", "uzik.poland.ns")
|
||||
cNNS.Invoke(t, false, "isAvailable", "uzik.animals")
|
||||
|
||||
records := stackitem.NewArray([]stackitem.Item{stackitem.NewBuffer([]byte("uzik.poland.ns")), stackitem.NewByteArray([]byte(base58.Encode(cnt2.id[:])))})
|
||||
cNNS.Invoke(t, records, "resolve", "uzik.animals", int64(nns.TXT))
|
||||
})
|
||||
|
||||
t.Run("gas costs are the same for all containers in block", func(t *testing.T) {
|
||||
const (
|
||||
containerPerBlock = 512
|
||||
|
|
|
@ -54,9 +54,6 @@ func TestNNSRegisterTLD(t *testing.T) {
|
|||
c.InvokeFail(t, "invalid domain name format", "register",
|
||||
"0com", c.CommitteeHash,
|
||||
"email@frostfs.info", refresh, retry, expire, ttl)
|
||||
c.InvokeFail(t, "invalid fragment '0com'", "register",
|
||||
"0com", c.CommitteeHash,
|
||||
"email@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
acc := c.NewAccount(t)
|
||||
cAcc := c.WithSigners(acc)
|
||||
|
@ -72,12 +69,6 @@ func TestNNSRegisterTLD(t *testing.T) {
|
|||
c.InvokeFail(t, "invalid domain name format", "register",
|
||||
"x", c.CommitteeHash,
|
||||
"email@frostfs.info", refresh, retry, expire, ttl)
|
||||
c.InvokeFail(t, "domain name too short", "register",
|
||||
"x", c.CommitteeHash,
|
||||
"email@frostfs.info", refresh, retry, expire, ttl)
|
||||
c.InvokeFail(t, "domain name too long", "register",
|
||||
getTooLongDomainName(255), c.CommitteeHash,
|
||||
"email@frostfs.info", refresh, retry, expire, ttl)
|
||||
})
|
||||
|
||||
c.Invoke(t, true, "register",
|
||||
|
@ -110,17 +101,9 @@ func TestNNSRegister(t *testing.T) {
|
|||
c3.InvokeFail(t, "invalid domain name format", "register",
|
||||
"-testdomain.com", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
c3.InvokeFail(t, "invalid fragment '-testdomain'", "register",
|
||||
"-testdomain.com", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c3.InvokeFail(t, "invalid domain name format", "register",
|
||||
"testdomain-.com", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
c3.InvokeFail(t, "invalid fragment 'testdomain-'", "register",
|
||||
"testdomain-.com", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c3.Invoke(t, true, "register",
|
||||
"test-domain.com", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
@ -159,49 +142,6 @@ func TestNNSRegister(t *testing.T) {
|
|||
c.Invoke(t, expected, "getRecords", "testdomain.com", int64(nns.TXT))
|
||||
}
|
||||
|
||||
func TestGlobalDomain(t *testing.T) {
|
||||
c := newNNSInvoker(t, false)
|
||||
|
||||
accTop := c.NewAccount(t)
|
||||
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
||||
c1 := c.WithSigners(c.Committee, accTop)
|
||||
c1.Invoke(t, true, "register",
|
||||
"com", accTop.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"testdomain.com", accTop.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"globaldomain.com", accTop.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"domik.testdomain.com", accTop.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
c1.Invoke(t, stackitem.Null{}, "addRecord",
|
||||
"domik.testdomain.com", int64(nns.TXT), "CID")
|
||||
|
||||
c.Invoke(t, true, "isAvailable", "domik.globaldomain.com")
|
||||
|
||||
c1.Invoke(t, stackitem.Null{}, "addRecord",
|
||||
"testdomain.com", int64(nns.TXT), nns.Cnametgt+"=globaldomain.com")
|
||||
c.Invoke(t, true, "isAvailable", "dom.testdomain.com")
|
||||
|
||||
c1.Invoke(t, stackitem.Null{}, "addRecord",
|
||||
"domik.testdomain.com", int64(nns.TXT), "random txt record")
|
||||
c.Invoke(t, true, "isAvailable", "domik.globaldomain.com")
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"dom.testdomain.com", accTop.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c1.Invoke(t, stackitem.Null{}, "addRecord",
|
||||
"dom.testdomain.com", int64(nns.TXT), "CID")
|
||||
|
||||
c.InvokeFail(t, "global domain is already taken", "isAvailable", "dom.testdomain.com")
|
||||
}
|
||||
func TestTLDRecord(t *testing.T) {
|
||||
c := newNNSInvoker(t, true)
|
||||
c.Invoke(t, stackitem.Null{}, "addRecord",
|
||||
|
@ -227,7 +167,7 @@ func TestNNSRegisterMulti(t *testing.T) {
|
|||
|
||||
c1 := c.WithSigners(acc)
|
||||
t.Run("parent domain is missing", func(t *testing.T) {
|
||||
msg := "domain does not exist or is expired: fs.neo.com"
|
||||
msg := "one of the parent domains is not registered"
|
||||
args[0] = "testnet.fs.neo.com"
|
||||
c1.InvokeFail(t, msg, "register", args...)
|
||||
})
|
||||
|
@ -402,38 +342,20 @@ func TestNNSIsAvailable(t *testing.T) {
|
|||
|
||||
acc := c.NewAccount(t)
|
||||
c1 := c.WithSigners(c.Committee, acc)
|
||||
|
||||
c1.InvokeFail(t, "domain does not exist or is expired: domain.com", "isAvailable", "dom.domain.com")
|
||||
c1.Invoke(t, true, "register",
|
||||
"domain.com", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"globaldomain.com", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c.Invoke(t, false, "isAvailable", "domain.com")
|
||||
|
||||
c.Invoke(t, true, "isAvailable", "dom.domain.com")
|
||||
c.InvokeFail(t, "domain does not exist or is expired: dom.domain.com", "isAvailable", "dom.dom.domain.com")
|
||||
c.InvokeFail(t, "parent does not exist or is expired", "isAvailable", "dom.dom.domain.com")
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"dom.domain.com", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
c.Invoke(t, false, "isAvailable", "dom.domain.com")
|
||||
c.Invoke(t, true, "isAvailable", "dom.dom.domain.com")
|
||||
|
||||
c1.Invoke(t, stackitem.Null{}, "addRecord",
|
||||
"dom.domain.com", int64(nns.TXT), nns.Cnametgt+"=globaldomain.com")
|
||||
c.Invoke(t, true, "isAvailable", "dom.dom.domain.com")
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"dom.globaldomain.com", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c.InvokeFail(t, "global domain is already taken", "isAvailable", "dom.dom.domain.com")
|
||||
|
||||
c.InvokeFail(t, "domain name too long", "isAvailable", getTooLongDomainName(255))
|
||||
}
|
||||
|
||||
func TestNNSRenew(t *testing.T) {
|
||||
|
@ -458,7 +380,6 @@ func TestNNSRenew(t *testing.T) {
|
|||
{Key: stackitem.Make("expiration"), Value: stackitem.Make(ts)},
|
||||
})
|
||||
cAcc.Invoke(t, expected, "properties", "testdomain.com")
|
||||
c.InvokeFail(t, "domain name too long", "renew", getTooLongDomainName(255))
|
||||
}
|
||||
|
||||
func TestNNSResolve(t *testing.T) {
|
||||
|
@ -508,11 +429,3 @@ func TestNNSAndProxy(t *testing.T) {
|
|||
checkBalance(t, c.CommitteeHash, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func getTooLongDomainName(max int) (res string) {
|
||||
for len(res) < max {
|
||||
res += "dom."
|
||||
}
|
||||
res += "com"
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -32,9 +32,6 @@ func newPolicyInvoker(t *testing.T) *neotest.ContractInvoker {
|
|||
func TestPolicy(t *testing.T) {
|
||||
e := newPolicyInvoker(t)
|
||||
|
||||
checkChainsIteratorByPrefix(t, e, policy.Namespace, "mynamespace", "ingress", [][]byte{})
|
||||
checkChainsIteratorByPrefix(t, e, policy.Container, "cnr1", "ingress", [][]byte{})
|
||||
|
||||
// Policies are opaque to the contract and are just raw bytes to store.
|
||||
p1 := []byte("chain1")
|
||||
p2 := []byte("chain2")
|
||||
|
|
Loading…
Reference in a new issue