Compare commits

..

1 commit

Author SHA1 Message Date
b4ca8cb3f9 [OBJECT-7758] Add ListFullSubjects method to Frostfsid client
Signed-off-by: d.zverev <d.zverev@yadro.com>
2024-03-14 13:01:42 +03:00
15 changed files with 394 additions and 794 deletions

View file

@ -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 }}'

View file

@ -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

View file

@ -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))
}
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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
}

View file

@ -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)

View file

@ -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 |
*/

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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)
}

View file

@ -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

View file

@ -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
}

View file

@ -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")