forked from TrueCloudLab/frostfs-contract
Compare commits
15 commits
bugfix/res
...
master
Author | SHA1 | Date | |
---|---|---|---|
454f4ed27c | |||
6922c70257 | |||
0653f0dbc6 | |||
201db45bd7 | |||
e002a1fa80 | |||
31709a3d95 | |||
3c7a9cb743 | |||
780b48cedf | |||
a3021f18cf | |||
891e268170 | |||
762d7f9f9f | |||
7e0f9b8b8e | |||
fe7a767e8f | |||
60b81c4bf6 | |||
a2c2791146 |
15 changed files with 249 additions and 154 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -1,6 +1,7 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
Changelog for FrostFS Contract
|
Changelog for FrostFS Contract
|
||||||
|
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -9,6 +10,21 @@ Changelog for FrostFS Contract
|
||||||
### Updated
|
### Updated
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
## [0.21.0]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Mention domain name in error messages in the nns contract (#92)
|
||||||
|
- Emit DeleteRecord event on record deletion in the nns contract (#114)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Allow to register TLD automatically (#114)
|
||||||
|
- Use frostfsid claims as a permission to create TLD (#115)
|
||||||
|
- Ensure subject keys are unique (#118, #129)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Terminate session in `ReadIteratorItems()` (#85)
|
||||||
|
- Declare `nns.getAllRecords` as safe (#131)
|
||||||
|
|
||||||
## [0.20.0]
|
## [0.20.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
v0.20.0
|
v0.21.2
|
||||||
|
|
|
@ -4,15 +4,15 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
major = 0
|
major = 0
|
||||||
minor = 20
|
minor = 21
|
||||||
patch = 0
|
patch = 2
|
||||||
|
|
||||||
// Versions from which an update should be performed.
|
// Versions from which an update should be performed.
|
||||||
// These should be used in a group (so prevMinor can be equal to minor if there are
|
// These should be used in a group (so prevMinor can be equal to minor if there are
|
||||||
// any migration routines.
|
// any migration routines.
|
||||||
prevMajor = 0
|
prevMajor = 0
|
||||||
prevMinor = 19
|
prevMinor = 19
|
||||||
prevPatch = 3
|
prevPatch = 4
|
||||||
|
|
||||||
Version = major*1_000_000 + minor*1_000 + patch
|
Version = major*1_000_000 + minor*1_000 + patch
|
||||||
|
|
||||||
|
@ -44,3 +44,8 @@ func AppendVersion(data any) []interface{} {
|
||||||
}
|
}
|
||||||
return append(data.([]any), Version)
|
return append(data.([]any), Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetVersion returns version as int by major, minor, patch.
|
||||||
|
func GetVersion(major, minor, patch int) int {
|
||||||
|
return major*1_000_000 + minor*1_000 + patch
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ permissions:
|
||||||
- "register"
|
- "register"
|
||||||
- "transferX"
|
- "transferX"
|
||||||
- "update"
|
- "update"
|
||||||
|
- "setAdmin"
|
||||||
|
|
||||||
events:
|
events:
|
||||||
- name: PutSuccess
|
- name: PutSuccess
|
||||||
|
@ -29,7 +30,7 @@ events:
|
||||||
- name: DeleteSuccess
|
- name: DeleteSuccess
|
||||||
parameters:
|
parameters:
|
||||||
- name: containerID
|
- name: containerID
|
||||||
type: ByteArray
|
type: Hash256
|
||||||
- name: SetEACLSuccess
|
- name: SetEACLSuccess
|
||||||
parameters:
|
parameters:
|
||||||
- name: containerID
|
- name: containerID
|
||||||
|
|
|
@ -279,7 +279,7 @@ func checkNiceNameAvailable(nnsContractAddr interop.Hash160, domain string) bool
|
||||||
// API.
|
// API.
|
||||||
//
|
//
|
||||||
// If the container doesn't exist, it panics with NotFoundError.
|
// If the container doesn't exist, it panics with NotFoundError.
|
||||||
func Delete(containerID []byte, signature interop.Signature, publicKey interop.PublicKey, token []byte) {
|
func Delete(containerID interop.Hash256, signature interop.Signature, publicKey interop.PublicKey, token []byte) {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
ownerID := getOwnerByID(ctx, containerID)
|
ownerID := getOwnerByID(ctx, containerID)
|
||||||
|
@ -565,6 +565,21 @@ func NewEpoch(epochNum int) {
|
||||||
cleanupContainers(ctx, epochNum)
|
cleanupContainers(ctx, epochNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAdmin sets admin for root container domain.
|
||||||
|
func SetAdmin(admin interop.Hash160) {
|
||||||
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
|
||||||
|
if !runtime.CheckWitness(common.CommitteeAddress()) {
|
||||||
|
panic("only committee can set admin")
|
||||||
|
}
|
||||||
|
|
||||||
|
addrNNS := storage.Get(ctx, nnsContractKey).(interop.Hash160)
|
||||||
|
rootContainerDomain := storage.Get(ctx, nnsRootKey).(string)
|
||||||
|
|
||||||
|
contract.Call(addrNNS, "setAdmin", contract.All,
|
||||||
|
rootContainerDomain, admin)
|
||||||
|
}
|
||||||
|
|
||||||
// StartContainerEstimation method produces StartEstimation notification.
|
// StartContainerEstimation method produces StartEstimation notification.
|
||||||
// It can be invoked only by Alphabet nodes of the Inner Ring.
|
// It can be invoked only by Alphabet nodes of the Inner Ring.
|
||||||
func StartContainerEstimation(epoch int) {
|
func StartContainerEstimation(epoch int) {
|
||||||
|
|
|
@ -47,12 +47,6 @@ events:
|
||||||
type: ByteArray
|
type: ByteArray
|
||||||
- name: keys
|
- name: keys
|
||||||
type: Array
|
type: Array
|
||||||
- name: AlphabetUpdate
|
|
||||||
parameters:
|
|
||||||
- name: id
|
|
||||||
type: ByteArray
|
|
||||||
- name: alphabet
|
|
||||||
type: Array
|
|
||||||
- name: SetConfig
|
- name: SetConfig
|
||||||
parameters:
|
parameters:
|
||||||
- name: id
|
- name: id
|
||||||
|
|
|
@ -103,7 +103,8 @@ func _deploy(data any, isUpdate bool) {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
args := data.(struct {
|
args := data.(struct {
|
||||||
admin interop.Hash160
|
admin interop.Hash160
|
||||||
|
version int
|
||||||
})
|
})
|
||||||
|
|
||||||
if args.admin != nil {
|
if args.admin != nil {
|
||||||
|
@ -114,24 +115,43 @@ func _deploy(data any, isUpdate bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if isUpdate {
|
if isUpdate {
|
||||||
it := storage.Find(ctx, subjectKeysPrefix, storage.ValuesOnly)
|
common.CheckVersion(args.version)
|
||||||
for iterator.Next(it) {
|
if args.version < common.GetVersion(0, 21, 1) {
|
||||||
subjectRaw := iterator.Value(it)
|
it := storage.Find(ctx, subjectKeysPrefix, storage.ValuesOnly)
|
||||||
subject := std.Deserialize(subjectRaw.([]byte)).(Subject)
|
for iterator.Next(it) {
|
||||||
address := addressKey(contract.CreateStandardAccount(subject.PrimaryKey))
|
subjectRaw := iterator.Value(it)
|
||||||
if storage.Get(ctx, address) != nil {
|
subject := std.Deserialize(subjectRaw.([]byte)).(Subject)
|
||||||
panic("frostfsid contract contains duplicate keys")
|
address := addressKey(contract.CreateStandardAccount(subject.PrimaryKey))
|
||||||
}
|
|
||||||
storage.Put(ctx, address, true)
|
|
||||||
|
|
||||||
for i := 0; i < len(subject.AdditionalKeys); i++ {
|
|
||||||
address = addressKey(contract.CreateStandardAccount(subject.AdditionalKeys[i]))
|
|
||||||
if storage.Get(ctx, address) != nil {
|
if storage.Get(ctx, address) != nil {
|
||||||
panic("frostfsid contract contains duplicate keys")
|
panic("frostfsid contract contains duplicate keys")
|
||||||
}
|
}
|
||||||
storage.Put(ctx, address, true)
|
storage.Put(ctx, address, true)
|
||||||
|
|
||||||
|
for i := 0; i < len(subject.AdditionalKeys); i++ {
|
||||||
|
address = addressKey(contract.CreateStandardAccount(subject.AdditionalKeys[i]))
|
||||||
|
if storage.Get(ctx, address) != nil {
|
||||||
|
panic("frostfsid contract contains duplicate keys")
|
||||||
|
}
|
||||||
|
storage.Put(ctx, address, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if args.version < common.GetVersion(0, 21, 2) {
|
||||||
|
maxGroupID := 0
|
||||||
|
it := storage.Find(ctx, groupKeysPrefix, storage.ValuesOnly)
|
||||||
|
for iterator.Next(it) {
|
||||||
|
groupRaw := iterator.Value(it)
|
||||||
|
group := std.Deserialize(groupRaw.([]byte)).(Group)
|
||||||
|
if group.ID > maxGroupID {
|
||||||
|
maxGroupID = group.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Put(ctx, groupCounterKey, maxGroupID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.Put(ctx, groupCounterKey, 0)
|
storage.Put(ctx, groupCounterKey, 0)
|
||||||
|
@ -399,6 +419,7 @@ func DeleteSubject(addr interop.Hash160) {
|
||||||
storage.Delete(ctx, subjectAdditionalKey(subj.AdditionalKeys[i], addr))
|
storage.Delete(ctx, subjectAdditionalKey(subj.AdditionalKeys[i], addr))
|
||||||
storage.Delete(ctx, addressKey(contract.CreateStandardAccount(subj.AdditionalKeys[i])))
|
storage.Delete(ctx, addressKey(contract.CreateStandardAccount(subj.AdditionalKeys[i])))
|
||||||
}
|
}
|
||||||
|
storage.Delete(ctx, addressKey(addr))
|
||||||
storage.Delete(ctx, sKey)
|
storage.Delete(ctx, sKey)
|
||||||
|
|
||||||
removeSubjectFromNamespace(ctx, subj.Namespace, addr)
|
removeSubjectFromNamespace(ctx, subj.Namespace, addr)
|
||||||
|
@ -417,7 +438,12 @@ func GetSubject(addr interop.Hash160) Subject {
|
||||||
sKey := subjectKeyFromAddr(addr)
|
sKey := subjectKeyFromAddr(addr)
|
||||||
data := storage.Get(ctx, sKey).([]byte)
|
data := storage.Get(ctx, sKey).([]byte)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
panic("subject not found")
|
a := getPrimaryAddr(ctx, addr)
|
||||||
|
sKey = subjectKeyFromAddr(a)
|
||||||
|
data = storage.Get(ctx, sKey).([]byte)
|
||||||
|
if data == nil {
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std.Deserialize(data).(Subject)
|
return std.Deserialize(data).(Subject)
|
||||||
|
@ -469,21 +495,25 @@ func GetSubjectByKey(key interop.PublicKey) Subject {
|
||||||
return std.Deserialize(data).(Subject)
|
return std.Deserialize(data).(Subject)
|
||||||
}
|
}
|
||||||
|
|
||||||
saPrefix := subjectAdditionalPrefix(key)
|
addr := getPrimaryAddr(ctx, contract.CreateStandardAccount(key))
|
||||||
it := storage.Find(ctx, saPrefix, storage.KeysOnly|storage.RemovePrefix)
|
sKey = subjectKeyFromAddr(addr)
|
||||||
for iterator.Next(it) {
|
data = storage.Get(ctx, sKey).([]byte)
|
||||||
addr := iterator.Value(it).([]byte)
|
if data != nil {
|
||||||
sKey = subjectKeyFromAddr(addr)
|
return std.Deserialize(data).(Subject)
|
||||||
data = storage.Get(ctx, sKey).([]byte)
|
|
||||||
if data != nil {
|
|
||||||
return std.Deserialize(data).(Subject)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("subject not found")
|
panic("subject not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPrimaryAddr(ctx storage.Context, addr interop.Hash160) interop.Hash160 {
|
||||||
|
saPrefix := append([]byte{additionalKeysPrefix}, addr...)
|
||||||
|
it := storage.Find(ctx, saPrefix, storage.KeysOnly|storage.RemovePrefix)
|
||||||
|
if iterator.Next(it) {
|
||||||
|
return iterator.Value(it).([]byte)
|
||||||
|
}
|
||||||
|
panic("subject not found")
|
||||||
|
}
|
||||||
|
|
||||||
// GetSubjectByName retrieves the subject with the specified name within the given namespace.
|
// GetSubjectByName retrieves the subject with the specified name within the given namespace.
|
||||||
func GetSubjectByName(ns, name string) Subject {
|
func GetSubjectByName(ns, name string) Subject {
|
||||||
key := GetSubjectKeyByName(ns, name)
|
key := GetSubjectKeyByName(ns, name)
|
||||||
|
|
|
@ -2,6 +2,7 @@ name: "NameService"
|
||||||
supportedstandards: ["NEP-11"]
|
supportedstandards: ["NEP-11"]
|
||||||
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf",
|
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf",
|
||||||
"tokens", "properties", "roots", "getPrice", "isAvailable", "getRecords",
|
"tokens", "properties", "roots", "getPrice", "isAvailable", "getRecords",
|
||||||
|
"getAllRecords",
|
||||||
"resolve", "version"]
|
"resolve", "version"]
|
||||||
events:
|
events:
|
||||||
- name: RegisterDomain
|
- name: RegisterDomain
|
||||||
|
|
|
@ -41,8 +41,8 @@ const (
|
||||||
// prefixRecord contains map from (token key + hash160(token name) + record type)
|
// prefixRecord contains map from (token key + hash160(token name) + record type)
|
||||||
// to record.
|
// to record.
|
||||||
prefixRecord byte = 0x22
|
prefixRecord byte = 0x22
|
||||||
//prefixGlobalDomain contains a flag indicating that this domain was created using GlobalDomain.
|
// prefixGlobalDomain contains a flag indicating that this domain was created using GlobalDomain.
|
||||||
//This is necessary to distinguish it from regular CNAME records.
|
// This is necessary to distinguish it from regular CNAME records.
|
||||||
prefixGlobalDomain byte = 0x23
|
prefixGlobalDomain byte = 0x23
|
||||||
// prefixCountSubDomains contains information about the number of domains in the zone.
|
// prefixCountSubDomains contains information about the number of domains in the zone.
|
||||||
// If it is nil, it will definitely be calculated on the first removal.
|
// If it is nil, it will definitely be calculated on the first removal.
|
||||||
|
@ -79,7 +79,7 @@ const (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Cnametgt is a special TXT record ensuring all created subdomains point to the global domain - the value of this variable.
|
// 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.
|
// It is guaranteed that two domains cannot point to the same global domain.
|
||||||
Cnametgt = "cnametgt"
|
Cnametgt = "cnametgt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ type PutSuccessEvent struct {
|
||||||
|
|
||||||
// DeleteSuccessEvent represents "DeleteSuccess" event emitted by the contract.
|
// DeleteSuccessEvent represents "DeleteSuccess" event emitted by the contract.
|
||||||
type DeleteSuccessEvent struct {
|
type DeleteSuccessEvent struct {
|
||||||
ContainerID []byte
|
ContainerID util.Uint256
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEACLSuccessEvent represents "SetEACLSuccess" event emitted by the contract.
|
// SetEACLSuccessEvent represents "SetEACLSuccess" event emitted by the contract.
|
||||||
|
@ -163,14 +163,14 @@ func (c *ContractReader) Version() (*big.Int, error) {
|
||||||
// Delete creates a transaction invoking `delete` method of the contract.
|
// Delete creates a transaction invoking `delete` method of the contract.
|
||||||
// This transaction is signed and immediately sent to the network.
|
// This transaction is signed and immediately sent to the network.
|
||||||
// The values returned are its hash, ValidUntilBlock value and error if any.
|
// The values returned are its hash, ValidUntilBlock value and error if any.
|
||||||
func (c *Contract) Delete(containerID []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (util.Uint256, uint32, error) {
|
func (c *Contract) Delete(containerID util.Uint256, signature []byte, publicKey *keys.PublicKey, token []byte) (util.Uint256, uint32, error) {
|
||||||
return c.actor.SendCall(c.hash, "delete", containerID, signature, publicKey, token)
|
return c.actor.SendCall(c.hash, "delete", containerID, signature, publicKey, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteTransaction creates a transaction invoking `delete` method of the contract.
|
// DeleteTransaction creates a transaction invoking `delete` method of the contract.
|
||||||
// This transaction is signed, but not sent to the network, instead it's
|
// This transaction is signed, but not sent to the network, instead it's
|
||||||
// returned to the caller.
|
// returned to the caller.
|
||||||
func (c *Contract) DeleteTransaction(containerID []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (*transaction.Transaction, error) {
|
func (c *Contract) DeleteTransaction(containerID util.Uint256, signature []byte, publicKey *keys.PublicKey, token []byte) (*transaction.Transaction, error) {
|
||||||
return c.actor.MakeCall(c.hash, "delete", containerID, signature, publicKey, token)
|
return c.actor.MakeCall(c.hash, "delete", containerID, signature, publicKey, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ func (c *Contract) DeleteTransaction(containerID []byte, signature []byte, publi
|
||||||
// This transaction is not signed, it's simply returned to the caller.
|
// 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,
|
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
|
||||||
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
|
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
|
||||||
func (c *Contract) DeleteUnsigned(containerID []byte, signature []byte, publicKey *keys.PublicKey, token []byte) (*transaction.Transaction, error) {
|
func (c *Contract) DeleteUnsigned(containerID util.Uint256, signature []byte, publicKey *keys.PublicKey, token []byte) (*transaction.Transaction, error) {
|
||||||
return c.actor.MakeUnsignedCall(c.hash, "delete", nil, containerID, signature, publicKey, token)
|
return c.actor.MakeUnsignedCall(c.hash, "delete", nil, containerID, signature, publicKey, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,6 +270,28 @@ func (c *Contract) PutNamedUnsigned(container []byte, signature []byte, publicKe
|
||||||
return c.actor.MakeUnsignedCall(c.hash, "putNamed", nil, container, signature, publicKey, token, name, zone)
|
return c.actor.MakeUnsignedCall(c.hash, "putNamed", nil, container, signature, publicKey, token, name, zone)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAdmin creates a transaction invoking `setAdmin` 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) SetAdmin(admin util.Uint160) (util.Uint256, uint32, error) {
|
||||||
|
return c.actor.SendCall(c.hash, "setAdmin", admin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAdminTransaction creates a transaction invoking `setAdmin` method of the contract.
|
||||||
|
// This transaction is signed, but not sent to the network, instead it's
|
||||||
|
// returned to the caller.
|
||||||
|
func (c *Contract) SetAdminTransaction(admin util.Uint160) (*transaction.Transaction, error) {
|
||||||
|
return c.actor.MakeCall(c.hash, "setAdmin", admin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAdminUnsigned creates a transaction invoking `setAdmin` 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) SetAdminUnsigned(admin util.Uint160) (*transaction.Transaction, error) {
|
||||||
|
return c.actor.MakeUnsignedCall(c.hash, "setAdmin", nil, admin)
|
||||||
|
}
|
||||||
|
|
||||||
// SetEACL creates a transaction invoking `setEACL` method of the contract.
|
// SetEACL creates a transaction invoking `setEACL` method of the contract.
|
||||||
// This transaction is signed and immediately sent to the network.
|
// This transaction is signed and immediately sent to the network.
|
||||||
// The values returned are its hash, ValidUntilBlock value and error if any.
|
// The values returned are its hash, ValidUntilBlock value and error if any.
|
||||||
|
@ -480,7 +502,17 @@ func (e *DeleteSuccessEvent) FromStackItem(item *stackitem.Array) error {
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
index++
|
index++
|
||||||
e.ContainerID, err = arr[index].TryBytes()
|
e.ContainerID, err = func(item stackitem.Item) (util.Uint256, error) {
|
||||||
|
b, err := item.TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint256{}, err
|
||||||
|
}
|
||||||
|
u, err := util.Uint256DecodeBytesBE(b)
|
||||||
|
if err != nil {
|
||||||
|
return util.Uint256{}, err
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}(arr[index])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("field ContainerID: %w", err)
|
return fmt.Errorf("field ContainerID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,12 +50,6 @@ type UnbindEvent struct {
|
||||||
Keys []any
|
Keys []any
|
||||||
}
|
}
|
||||||
|
|
||||||
// AlphabetUpdateEvent represents "AlphabetUpdate" event emitted by the contract.
|
|
||||||
type AlphabetUpdateEvent struct {
|
|
||||||
Id []byte
|
|
||||||
Alphabet []any
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetConfigEvent represents "SetConfig" event emitted by the contract.
|
// SetConfigEvent represents "SetConfig" event emitted by the contract.
|
||||||
type SetConfigEvent struct {
|
type SetConfigEvent struct {
|
||||||
Id []byte
|
Id []byte
|
||||||
|
@ -710,77 +704,6 @@ func (e *UnbindEvent) FromStackItem(item *stackitem.Array) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AlphabetUpdateEventsFromApplicationLog retrieves a set of all emitted events
|
|
||||||
// with "AlphabetUpdate" name from the provided [result.ApplicationLog].
|
|
||||||
func AlphabetUpdateEventsFromApplicationLog(log *result.ApplicationLog) ([]*AlphabetUpdateEvent, error) {
|
|
||||||
if log == nil {
|
|
||||||
return nil, errors.New("nil application log")
|
|
||||||
}
|
|
||||||
|
|
||||||
var res []*AlphabetUpdateEvent
|
|
||||||
for i, ex := range log.Executions {
|
|
||||||
for j, e := range ex.Events {
|
|
||||||
if e.Name != "AlphabetUpdate" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
event := new(AlphabetUpdateEvent)
|
|
||||||
err := event.FromStackItem(e.Item)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to deserialize AlphabetUpdateEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
|
|
||||||
}
|
|
||||||
res = append(res, event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromStackItem converts provided [stackitem.Array] to AlphabetUpdateEvent or
|
|
||||||
// returns an error if it's not possible to do to so.
|
|
||||||
func (e *AlphabetUpdateEvent) FromStackItem(item *stackitem.Array) error {
|
|
||||||
if item == nil {
|
|
||||||
return errors.New("nil item")
|
|
||||||
}
|
|
||||||
arr, ok := item.Value().([]stackitem.Item)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("not an array")
|
|
||||||
}
|
|
||||||
if len(arr) != 2 {
|
|
||||||
return errors.New("wrong number of structure elements")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
index = -1
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
index++
|
|
||||||
e.Id, err = arr[index].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("field Id: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
index++
|
|
||||||
e.Alphabet, err = func(item stackitem.Item) ([]any, error) {
|
|
||||||
arr, ok := item.Value().([]stackitem.Item)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("not an array")
|
|
||||||
}
|
|
||||||
res := make([]any, len(arr))
|
|
||||||
for i := range res {
|
|
||||||
res[i], err = arr[i].Value(), error(nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("item %d: %w", i, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}(arr[index])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("field Alphabet: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetConfigEventsFromApplicationLog retrieves a set of all emitted events
|
// SetConfigEventsFromApplicationLog retrieves a set of all emitted events
|
||||||
// with "SetConfig" name from the provided [result.ApplicationLog].
|
// with "SetConfig" name from the provided [result.ApplicationLog].
|
||||||
func SetConfigEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetConfigEvent, error) {
|
func SetConfigEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetConfigEvent, error) {
|
||||||
|
|
|
@ -91,6 +91,20 @@ func New(actor Actor, hash util.Uint160) *Contract {
|
||||||
return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor, hash}, nep11ndt.BaseWriter, actor, hash}
|
return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor, hash}, nep11ndt.BaseWriter, actor, hash}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllRecords invokes `getAllRecords` method of contract.
|
||||||
|
func (c *ContractReader) GetAllRecords(name string) (uuid.UUID, result.Iterator, error) {
|
||||||
|
return unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract
|
||||||
|
// method), but can be useful if the server used doesn't support sessions and
|
||||||
|
// doesn't expand iterators. It creates a script that will get the specified
|
||||||
|
// number of result items from the iterator right in the VM and return them to
|
||||||
|
// you. It's only limited by VM stack and GAS available for RPC invocations.
|
||||||
|
func (c *ContractReader) GetAllRecordsExpanded(name string, _numOfIteratorItems int) ([]stackitem.Item, error) {
|
||||||
|
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "getAllRecords", _numOfIteratorItems, name))
|
||||||
|
}
|
||||||
|
|
||||||
// GetPrice invokes `getPrice` method of contract.
|
// GetPrice invokes `getPrice` method of contract.
|
||||||
func (c *ContractReader) GetPrice() (*big.Int, error) {
|
func (c *ContractReader) GetPrice() (*big.Int, error) {
|
||||||
return unwrap.BigInt(c.invoker.Call(c.hash, "getPrice"))
|
return unwrap.BigInt(c.invoker.Call(c.hash, "getPrice"))
|
||||||
|
@ -234,28 +248,6 @@ func (c *Contract) DeleteRecordsUnsigned(name string, typ *big.Int) (*transactio
|
||||||
return c.actor.MakeUnsignedCall(c.hash, "deleteRecords", nil, name, typ)
|
return c.actor.MakeUnsignedCall(c.hash, "deleteRecords", nil, name, typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllRecords creates a transaction invoking `getAllRecords` 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) GetAllRecords(name string) (util.Uint256, uint32, error) {
|
|
||||||
return c.actor.SendCall(c.hash, "getAllRecords", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllRecordsTransaction creates a transaction invoking `getAllRecords` method of the contract.
|
|
||||||
// This transaction is signed, but not sent to the network, instead it's
|
|
||||||
// returned to the caller.
|
|
||||||
func (c *Contract) GetAllRecordsTransaction(name string) (*transaction.Transaction, error) {
|
|
||||||
return c.actor.MakeCall(c.hash, "getAllRecords", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllRecordsUnsigned creates a transaction invoking `getAllRecords` 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) GetAllRecordsUnsigned(name string) (*transaction.Transaction, error) {
|
|
||||||
return c.actor.MakeUnsignedCall(c.hash, "getAllRecords", nil, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Contract) scriptForRegister(name string, owner util.Uint160, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) ([]byte, error) {
|
func (c *Contract) scriptForRegister(name string, owner util.Uint160, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) ([]byte, error) {
|
||||||
return smartcontract.CreateCallWithAssertScript(c.hash, "register", name, owner, email, refresh, retry, expire, ttl)
|
return smartcontract.CreateCallWithAssertScript(c.hash, "register", name, owner, email, refresh, retry, expire, ttl)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,13 +28,17 @@ const (
|
||||||
containerAliasFee = 0o_0050_0000
|
containerAliasFee = 0o_0050_0000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
containerRootDomain = "frostfs"
|
||||||
|
)
|
||||||
|
|
||||||
func deployContainerContract(t *testing.T, e *neotest.Executor, addrNetmap, addrBalance, addrNNS util.Uint160) util.Uint160 {
|
func deployContainerContract(t *testing.T, e *neotest.Executor, addrNetmap, addrBalance, addrNNS util.Uint160) util.Uint160 {
|
||||||
args := make([]any, 5)
|
args := make([]any, 5)
|
||||||
args[0] = addrNetmap
|
args[0] = addrNetmap
|
||||||
args[1] = addrBalance
|
args[1] = addrBalance
|
||||||
args[2] = util.Uint160{} // not needed for now
|
args[2] = util.Uint160{} // not needed for now
|
||||||
args[3] = addrNNS
|
args[3] = addrNNS
|
||||||
args[4] = "frostfs"
|
args[4] = containerRootDomain
|
||||||
|
|
||||||
c := neotest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml"))
|
c := neotest.CompileFile(t, e.CommitteeHash, containerPath, path.Join(containerPath, "config.yml"))
|
||||||
e.DeployContract(t, c, args)
|
e.DeployContract(t, c, args)
|
||||||
|
@ -207,14 +211,14 @@ func TestContainerPut(t *testing.T) {
|
||||||
stackitem.NewByteArray([]byte(base58.Encode(cnt.id[:]))),
|
stackitem.NewByteArray([]byte(base58.Encode(cnt.id[:]))),
|
||||||
})
|
})
|
||||||
cNNS := c.CommitteeInvoker(nnsHash)
|
cNNS := c.CommitteeInvoker(nnsHash)
|
||||||
cNNS.Invoke(t, expected, "resolve", "mycnt.frostfs", int64(nns.TXT))
|
cNNS.Invoke(t, expected, "resolve", "mycnt."+containerRootDomain, int64(nns.TXT))
|
||||||
|
|
||||||
t.Run("name is already taken", func(t *testing.T) {
|
t.Run("name is already taken", func(t *testing.T) {
|
||||||
c.InvokeFail(t, "name is already taken", "putNamed", putArgs...)
|
c.InvokeFail(t, "name is already taken", "putNamed", putArgs...)
|
||||||
})
|
})
|
||||||
|
|
||||||
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
|
c.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
|
||||||
cNNS.Invoke(t, stackitem.Null{}, "resolve", "mycnt.frostfs", int64(nns.TXT))
|
cNNS.Invoke(t, stackitem.Null{}, "resolve", "mycnt."+containerRootDomain, int64(nns.TXT))
|
||||||
|
|
||||||
t.Run("register in advance", func(t *testing.T) {
|
t.Run("register in advance", func(t *testing.T) {
|
||||||
cnt.value[len(cnt.value)-1] = 10
|
cnt.value[len(cnt.value)-1] = 10
|
||||||
|
@ -241,6 +245,16 @@ func TestContainerPut(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("test setAdmin", func(t *testing.T) {
|
||||||
|
ctrNNS := neotest.CompileFile(t, c.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
|
||||||
|
nnsInv := c.NewInvoker(ctrNNS.Hash, acc)
|
||||||
|
containerInv := c.WithSigners(c.Committee, acc)
|
||||||
|
|
||||||
|
nnsInv.InvokeFail(t, "not witnessed by admin", "addRecord", containerRootDomain, int64(nns.TXT), nns.Cnametgt+"=animals")
|
||||||
|
containerInv.Invoke(t, stackitem.Null{}, "setAdmin", acc.ScriptHash())
|
||||||
|
nnsInv.Invoke(t, stackitem.Null{}, "addRecord", containerRootDomain, int64(nns.TXT), nns.Cnametgt+"=animals")
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("create global domain", func(t *testing.T) {
|
t.Run("create global domain", func(t *testing.T) {
|
||||||
ctrNNS := neotest.CompileFile(t, c.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
|
ctrNNS := neotest.CompileFile(t, c.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
|
||||||
nnsHash := ctrNNS.Hash
|
nnsHash := ctrNNS.Hash
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package tests
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -238,6 +240,13 @@ func TestFrostFSID_SubjectManagement(t *testing.T) {
|
||||||
subj = parseSubject(t, s)
|
subj = parseSubject(t, s)
|
||||||
require.True(t, subjKey.PublicKey().Equal(&subj.PrimaryKey), "keys must be the same")
|
require.True(t, subjKey.PublicKey().Equal(&subj.PrimaryKey), "keys must be the same")
|
||||||
|
|
||||||
|
t.Run("with GetSubject", func(t *testing.T) {
|
||||||
|
s, err = anonInvoker.TestInvoke(t, getSubjectMethod, subjKey.PublicKey().GetScriptHash())
|
||||||
|
require.NoError(t, err)
|
||||||
|
subj = parseSubject(t, s)
|
||||||
|
require.True(t, subjKey.PublicKey().Equal(&subj.PrimaryKey), "keys must be the same")
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("remove subject key", func(t *testing.T) {
|
t.Run("remove subject key", func(t *testing.T) {
|
||||||
anonInvoker.InvokeFail(t, notWitnessedError, removeSubjectKeyMethod, subjKeyAddr, subjNewKey.PublicKey().Bytes())
|
anonInvoker.InvokeFail(t, notWitnessedError, removeSubjectKeyMethod, subjKeyAddr, subjNewKey.PublicKey().Bytes())
|
||||||
invoker.Invoke(t, stackitem.Null{}, removeSubjectKeyMethod, subjKeyAddr, subjNewKey.PublicKey().Bytes())
|
invoker.Invoke(t, stackitem.Null{}, removeSubjectKeyMethod, subjKeyAddr, subjNewKey.PublicKey().Bytes())
|
||||||
|
@ -622,6 +631,9 @@ func TestAdditionalKeyFromPrimarySubject(t *testing.T) {
|
||||||
subjDPrimaryKey, err := keys.NewPrivateKey()
|
subjDPrimaryKey, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
subjFPrimaryKey, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
invoker.Invoke(t, stackitem.Null{}, createSubjectMethod, defaultNamespace, subjAPrimaryKey.PublicKey().Bytes())
|
invoker.Invoke(t, stackitem.Null{}, createSubjectMethod, defaultNamespace, subjAPrimaryKey.PublicKey().Bytes())
|
||||||
|
|
||||||
invoker.Invoke(t, stackitem.Null{}, createSubjectMethod, defaultNamespace, subjBPrimaryKey.PublicKey().Bytes())
|
invoker.Invoke(t, stackitem.Null{}, createSubjectMethod, defaultNamespace, subjBPrimaryKey.PublicKey().Bytes())
|
||||||
|
@ -640,6 +652,57 @@ func TestAdditionalKeyFromPrimarySubject(t *testing.T) {
|
||||||
invoker.InvokeFail(t, "key is occupied", addSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
invoker.InvokeFail(t, "key is occupied", addSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||||
invoker.Invoke(t, stackitem.Null{}, deleteSubjectMethod, subjBKeyAddr)
|
invoker.Invoke(t, stackitem.Null{}, deleteSubjectMethod, subjBKeyAddr)
|
||||||
invoker.Invoke(t, stackitem.Null{}, addSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
invoker.Invoke(t, stackitem.Null{}, addSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||||
|
|
||||||
|
invoker.Invoke(t, stackitem.Null{}, createSubjectMethod, defaultNamespace, subjFPrimaryKey.PublicKey().Bytes())
|
||||||
|
invoker.Invoke(t, stackitem.Null{}, deleteSubjectMethod, subjFPrimaryKey.PublicKey().GetScriptHash())
|
||||||
|
invoker.Invoke(t, stackitem.Null{}, createSubjectMethod, defaultNamespace, subjFPrimaryKey.PublicKey().Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFrostFSIS_GroupsAfterUpdate(t *testing.T) {
|
||||||
|
countNamesepce := 2
|
||||||
|
f := newFrostFSIDInvoker(t)
|
||||||
|
invoker := f.OwnerInvoker()
|
||||||
|
|
||||||
|
groupid := int64(1)
|
||||||
|
|
||||||
|
for i := range countNamesepce {
|
||||||
|
namespace := fmt.Sprintf("nm_%d", i)
|
||||||
|
invoker.Invoke(t, stackitem.Null{}, createNamespaceMethod, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range 5 {
|
||||||
|
namespace := fmt.Sprintf("nm_%d", i%2)
|
||||||
|
groupName := fmt.Sprintf("group_%d", groupid)
|
||||||
|
|
||||||
|
invoker.Invoke(t, stackitem.Make(groupid), createGroupMethod, namespace, groupName)
|
||||||
|
s, err := invoker.TestInvoke(t, getGroupIDByNameMethod, namespace, groupName)
|
||||||
|
checkGroupIDResult(t, s, err, groupid)
|
||||||
|
groupid++
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]any, 2)
|
||||||
|
args[0] = f.owner.ScriptHash()
|
||||||
|
args[1] = 21 * 1_000
|
||||||
|
|
||||||
|
c := neotest.CompileFile(t, f.e.CommitteeHash, frostfsidPath, path.Join(frostfsidPath, "config.yml"))
|
||||||
|
|
||||||
|
nef, err := c.NEF.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
manifest, err := json.Marshal(c.Manifest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
f.CommitteeInvoker().Invoke(t, stackitem.Null{}, "update", nef, manifest, args)
|
||||||
|
|
||||||
|
for i := range 5 {
|
||||||
|
namespace := fmt.Sprintf("nm_%d", i%2)
|
||||||
|
groupName := fmt.Sprintf("group_%d", groupid)
|
||||||
|
|
||||||
|
invoker.Invoke(t, stackitem.Make(groupid), createGroupMethod, namespace, groupName)
|
||||||
|
s, err := invoker.TestInvoke(t, getGroupIDByNameMethod, namespace, groupName)
|
||||||
|
checkGroupIDResult(t, s, err, groupid)
|
||||||
|
groupid++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkPublicKeyResult(t *testing.T, s *vm.Stack, err error, key *keys.PrivateKey) {
|
func checkPublicKeyResult(t *testing.T, s *vm.Stack, err error, key *keys.PrivateKey) {
|
||||||
|
|
|
@ -177,8 +177,10 @@ func TestNNSRegister(t *testing.T) {
|
||||||
|
|
||||||
cAcc := c.WithSigners(acc)
|
cAcc := c.WithSigners(acc)
|
||||||
|
|
||||||
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("testdomain.com")),
|
expected = stackitem.NewArray([]stackitem.Item{
|
||||||
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT)))})
|
stackitem.NewByteArray([]byte("testdomain.com")),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT))),
|
||||||
|
})
|
||||||
tx = cAcc.Invoke(t, stackitem.Null{}, "addRecord",
|
tx = cAcc.Invoke(t, stackitem.Null{}, "addRecord",
|
||||||
"testdomain.com", int64(nns.TXT), "first TXT record")
|
"testdomain.com", int64(nns.TXT), "first TXT record")
|
||||||
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "AddRecord", Item: expected})
|
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "AddRecord", Item: expected})
|
||||||
|
@ -194,8 +196,10 @@ func TestNNSRegister(t *testing.T) {
|
||||||
})
|
})
|
||||||
c.Invoke(t, expected, "getRecords", "testdomain.com", int64(nns.TXT))
|
c.Invoke(t, expected, "getRecords", "testdomain.com", int64(nns.TXT))
|
||||||
|
|
||||||
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("testdomain.com")),
|
expected = stackitem.NewArray([]stackitem.Item{
|
||||||
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT)))})
|
stackitem.NewByteArray([]byte("testdomain.com")),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT))),
|
||||||
|
})
|
||||||
tx = cAcc.Invoke(t, stackitem.Null{}, "setRecord",
|
tx = cAcc.Invoke(t, stackitem.Null{}, "setRecord",
|
||||||
"testdomain.com", int64(nns.TXT), int64(0), "replaced first")
|
"testdomain.com", int64(nns.TXT), int64(0), "replaced first")
|
||||||
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "AddRecord", Item: expected})
|
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "AddRecord", Item: expected})
|
||||||
|
@ -207,8 +211,10 @@ func TestNNSRegister(t *testing.T) {
|
||||||
c.Invoke(t, expected, "getRecords", "testdomain.com", int64(nns.TXT))
|
c.Invoke(t, expected, "getRecords", "testdomain.com", int64(nns.TXT))
|
||||||
|
|
||||||
tx = cAcc.Invoke(t, stackitem.Null{}, "deleteRecords", "testdomain.com", int64(nns.TXT))
|
tx = cAcc.Invoke(t, stackitem.Null{}, "deleteRecords", "testdomain.com", int64(nns.TXT))
|
||||||
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("testdomain.com")),
|
expected = stackitem.NewArray([]stackitem.Item{
|
||||||
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT)))})
|
stackitem.NewByteArray([]byte("testdomain.com")),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT))),
|
||||||
|
})
|
||||||
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "DeleteRecords", Item: expected})
|
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "DeleteRecords", Item: expected})
|
||||||
|
|
||||||
c.Invoke(t, stackitem.Null{}, "getRecords", "testdomain.com", int64(nns.TXT))
|
c.Invoke(t, stackitem.Null{}, "getRecords", "testdomain.com", int64(nns.TXT))
|
||||||
|
@ -255,8 +261,10 @@ func TestNNSRegister(t *testing.T) {
|
||||||
c.Invoke(t, stackitem.Null{}, "getRecords", "testdomain.com", int64(nns.TXT))
|
c.Invoke(t, stackitem.Null{}, "getRecords", "testdomain.com", int64(nns.TXT))
|
||||||
|
|
||||||
tx = cAcc.Invoke(t, stackitem.Null{}, "deleteDomain", "testdomain.com")
|
tx = cAcc.Invoke(t, stackitem.Null{}, "deleteDomain", "testdomain.com")
|
||||||
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("testdomain.com")),
|
expected = stackitem.NewArray([]stackitem.Item{
|
||||||
stackitem.NewBigInteger(big.NewInt(int64(nns.CNAME)))})
|
stackitem.NewByteArray([]byte("testdomain.com")),
|
||||||
|
stackitem.NewBigInteger(big.NewInt(int64(nns.CNAME))),
|
||||||
|
})
|
||||||
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "DeleteRecords", Item: expected})
|
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "DeleteRecords", Item: expected})
|
||||||
|
|
||||||
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("testdomain.com"))})
|
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("testdomain.com"))})
|
||||||
|
@ -371,6 +379,7 @@ func TestGlobalDomain(t *testing.T) {
|
||||||
|
|
||||||
c.InvokeFail(t, "global domain is already taken", "isAvailable", "dom.testdomain.com")
|
c.InvokeFail(t, "global domain is already taken", "isAvailable", "dom.testdomain.com")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTLDRecord(t *testing.T) {
|
func TestTLDRecord(t *testing.T) {
|
||||||
c := newNNSInvoker(t, true)
|
c := newNNSInvoker(t, true)
|
||||||
c.Invoke(t, stackitem.Null{}, "addRecord",
|
c.Invoke(t, stackitem.Null{}, "addRecord",
|
||||||
|
|
Loading…
Add table
Reference in a new issue