Compare commits
No commits in common. "master" and "fix/change_gudz" have entirely different histories.
master
...
fix/change
28 changed files with 103 additions and 712 deletions
|
@ -13,7 +13,7 @@ jobs:
|
|||
- name: Setup Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.23'
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Run commit format checker
|
||||
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
|
||||
|
|
|
@ -7,7 +7,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go_versions: [ '1.22', '1.23' ]
|
||||
go_versions: [ '1.21', '1.22' ]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
|
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
* @carpawell @fyrchik @cthulhu-rider
|
0
.forgejo/logo.svg → .github/logo.svg
vendored
0
.forgejo/logo.svg → .github/logo.svg
vendored
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -8,17 +8,7 @@ Changelog for FrostFS Contract
|
|||
### Removed
|
||||
### Updated
|
||||
### Fixed
|
||||
|
||||
## [0.20.0]
|
||||
|
||||
### Added
|
||||
- Add `ListFullSubjects` method to the frostfsid RPC client (#107)
|
||||
- Add `ListChainNames` method to the policy contract (#105)
|
||||
- Add `DeleteRecord` method to the nns contract (#114)
|
||||
- Emit notification on record changes in nns contract (#109)
|
||||
|
||||
### Updated
|
||||
- neo-go to v0.106.3
|
||||
### Updating from v0.18.0
|
||||
|
||||
## [0.18.0] - 2023-09-14 - Academy of Sciences Glacier
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
.forgejo/.* @potyarkin
|
||||
Makefile @potyarkin
|
||||
frostfsid/client/.* @dkirillov
|
||||
.* @TrueCloudLab/storage-core-developers @TrueCloudLab/storage-core-committers @TrueCloudLab/storage-service-developers @TrueCloudLab/storage-service-committers
|
||||
tests/.* @fyrchik
|
|
@ -1,5 +1,5 @@
|
|||
<p align="center">
|
||||
<img src="./.forgejo/logo.svg" width="500px" alt="FrostFS">
|
||||
<img src="./.github/logo.svg" width="500px" alt="FrostFS">
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://frostfs.info">FrostFS</a> related smart contracts.
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
v0.20.0
|
||||
v0.19.1
|
||||
|
|
|
@ -4,15 +4,15 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
|
|||
|
||||
const (
|
||||
major = 0
|
||||
minor = 20
|
||||
patch = 0
|
||||
minor = 19
|
||||
patch = 1
|
||||
|
||||
// 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
|
||||
// any migration routines.
|
||||
prevMajor = 0
|
||||
prevMinor = 19
|
||||
prevPatch = 3
|
||||
prevMinor = 18
|
||||
prevPatch = 0
|
||||
|
||||
Version = major*1_000_000 + minor*1_000 + patch
|
||||
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
package commonclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
)
|
||||
|
||||
const alreadyExistsError = "already exists"
|
||||
|
||||
type WaiterOptions struct {
|
||||
// IgnoreAlreadyExistsError controls behavior for "already exists" error:
|
||||
// - If set to true, it indicates that "already exists" error is not a problem, we should
|
||||
// wait for transaction as usual (this is the behavior of neo-go [waiter.PollingBased]).
|
||||
// - If set to false, it indicates that "already exists" should be reported as an error.
|
||||
IgnoreAlreadyExistsError bool
|
||||
|
||||
// VerifyExecResults controls whether waiter should ensure that transaction successfully
|
||||
// enters blockchain block.
|
||||
VerifyExecResults bool
|
||||
}
|
||||
|
||||
// Waiter is a decorator on top of the standard [waiter.Waiter].
|
||||
// It provides additional behavior (controlled by [WaiterOptions]) on top of the standard
|
||||
// functionality of awaiting transactions.
|
||||
type Waiter struct {
|
||||
waiter waiter.Waiter
|
||||
options WaiterOptions
|
||||
}
|
||||
|
||||
var _ waiter.Waiter = (*Waiter)(nil)
|
||||
|
||||
// NewWaiter decorates the specified waiter in a new [Waiter] instance.
|
||||
func NewWaiter(waiter waiter.Waiter, options WaiterOptions) *Waiter {
|
||||
return &Waiter{
|
||||
waiter: waiter,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// Wait allows to wait until transaction will be accepted to the chain.
|
||||
func (w *Waiter) Wait(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
|
||||
if !w.options.IgnoreAlreadyExistsError && errIsAlreadyExists(err) {
|
||||
return nil, err
|
||||
}
|
||||
result, err := w.waiter.Wait(h, vub, err)
|
||||
return w.examineExecResult(result, err)
|
||||
}
|
||||
|
||||
// WaitAny waits until at least one of the specified transactions will be accepted
|
||||
// to the chain.
|
||||
func (w *Waiter) WaitAny(ctx context.Context, vub uint32, hashes ...util.Uint256) (*state.AppExecResult, error) {
|
||||
result, err := w.waiter.WaitAny(ctx, vub, hashes...)
|
||||
return w.examineExecResult(result, err)
|
||||
}
|
||||
|
||||
func (w *Waiter) examineExecResult(result *state.AppExecResult, err error) (*state.AppExecResult, error) {
|
||||
if !w.options.VerifyExecResults || err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
if result.Execution.VMState != vmstate.Fault {
|
||||
// Transaction didn't fail, so we just return result "as is"
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Transaction failed, we extract VM exception from it and report as an error
|
||||
if result.FaultException != "" {
|
||||
return result, fmt.Errorf("%s", result.FaultException)
|
||||
}
|
||||
return result, fmt.Errorf("transaction failed, stack=%v", result.Stack)
|
||||
}
|
||||
|
||||
func errIsAlreadyExists(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(strings.ToLower(err.Error()), alreadyExistsError)
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
package commonclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type mockWaiter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (w *mockWaiter) successfulResult(txHash util.Uint256) *state.AppExecResult {
|
||||
return &state.AppExecResult{
|
||||
Container: txHash,
|
||||
Execution: state.Execution{
|
||||
Trigger: trigger.Application,
|
||||
VMState: vmstate.Halt,
|
||||
GasConsumed: 100500,
|
||||
Stack: nil,
|
||||
Events: nil,
|
||||
FaultException: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (w *mockWaiter) failedResult(txHash util.Uint256, exception string) *state.AppExecResult {
|
||||
return &state.AppExecResult{
|
||||
Container: txHash,
|
||||
Execution: state.Execution{
|
||||
Trigger: trigger.Application,
|
||||
VMState: vmstate.Fault,
|
||||
GasConsumed: 100500,
|
||||
Stack: nil,
|
||||
Events: nil,
|
||||
FaultException: exception,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockWaiter) Wait(h util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
|
||||
args := m.Called(h, vub, err)
|
||||
result := args.Get(0)
|
||||
if result == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return result.(*state.AppExecResult), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *mockWaiter) WaitAny(ctx context.Context, vub uint32, hashes ...util.Uint256) (*state.AppExecResult, error) {
|
||||
args := m.Called(ctx, vub, hashes)
|
||||
result := args.Get(0)
|
||||
if result == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return result.(*state.AppExecResult), args.Error(1)
|
||||
}
|
||||
|
||||
func TestWaiter(t *testing.T) {
|
||||
txHash := util.Uint256{}
|
||||
vub := uint32(100)
|
||||
|
||||
t.Run("ignore already exists error", func(t *testing.T) {
|
||||
sendErr := fmt.Errorf("transaction already exists")
|
||||
mw := &mockWaiter{}
|
||||
mw.On("Wait", txHash, vub, mock.Anything).Return(mw.successfulResult(txHash), nil)
|
||||
|
||||
waiter := NewWaiter(mw, WaiterOptions{IgnoreAlreadyExistsError: true})
|
||||
_, err := waiter.Wait(txHash, vub, sendErr)
|
||||
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("report already exists error", func(t *testing.T) {
|
||||
sendErr := fmt.Errorf("transaction already exists")
|
||||
mw := &mockWaiter{}
|
||||
mw.On("Wait", txHash, vub, mock.Anything).Return(mw.successfulResult(txHash), nil)
|
||||
|
||||
waiter := NewWaiter(mw, WaiterOptions{IgnoreAlreadyExistsError: false})
|
||||
_, err := waiter.Wait(txHash, vub, sendErr)
|
||||
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("report wait error when transaction error is ignored", func(t *testing.T) {
|
||||
waitErr := fmt.Errorf("mock error")
|
||||
mw := &mockWaiter{}
|
||||
mw.On("Wait", txHash, vub, nil).Return(nil, waitErr)
|
||||
|
||||
waiter := NewWaiter(mw, WaiterOptions{VerifyExecResults: false})
|
||||
_, err := waiter.Wait(txHash, vub, nil)
|
||||
|
||||
require.ErrorIs(t, err, waitErr)
|
||||
})
|
||||
|
||||
t.Run("report wait error when transaction error is verified", func(t *testing.T) {
|
||||
waitErr := fmt.Errorf("mock error")
|
||||
mw := &mockWaiter{}
|
||||
mw.On("Wait", txHash, vub, nil).Return(nil, waitErr)
|
||||
|
||||
waiter := NewWaiter(mw, WaiterOptions{VerifyExecResults: true})
|
||||
_, err := waiter.Wait(txHash, vub, nil)
|
||||
|
||||
require.ErrorIs(t, err, waitErr)
|
||||
})
|
||||
|
||||
t.Run("ignore error from transaction", func(t *testing.T) {
|
||||
txError := "mock error"
|
||||
mw := &mockWaiter{}
|
||||
mw.On("Wait", txHash, vub, nil).Return(mw.failedResult(txHash, txError), nil)
|
||||
|
||||
waiter := NewWaiter(mw, WaiterOptions{VerifyExecResults: false})
|
||||
_, err := waiter.Wait(txHash, vub, nil)
|
||||
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("examine error from transaction", func(t *testing.T) {
|
||||
txError := "mock error"
|
||||
mw := &mockWaiter{}
|
||||
mw.On("Wait", txHash, vub, nil).Return(mw.failedResult(txHash, txError), nil)
|
||||
|
||||
waiter := NewWaiter(mw, WaiterOptions{VerifyExecResults: true})
|
||||
_, err := waiter.Wait(txHash, vub, nil)
|
||||
|
||||
require.ErrorContains(t, err, txError)
|
||||
})
|
||||
}
|
|
@ -23,13 +23,11 @@ import (
|
|||
type (
|
||||
Client struct {
|
||||
act *actor.Actor
|
||||
waiter waiter.Waiter
|
||||
contract util.Uint160
|
||||
}
|
||||
|
||||
Options struct {
|
||||
ProxyContract util.Uint160
|
||||
Waiter commonclient.WaiterOptions
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -96,7 +94,6 @@ const (
|
|||
createSubjectMethod = "createSubject"
|
||||
getSubjectMethod = "getSubject"
|
||||
getSubjectExtendedMethod = "getSubjectExtended"
|
||||
getSubjectKVMethod = "getSubjectKV"
|
||||
listSubjectsMethod = "listSubjects"
|
||||
addSubjectKeyMethod = "addSubjectKey"
|
||||
removeSubjectKeyMethod = "removeSubjectKey"
|
||||
|
@ -154,21 +151,17 @@ func New(ra actor.RPCActor, acc *wallet.Account, contract util.Uint160, opt Opti
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("init actor: %w", err)
|
||||
}
|
||||
wtr := commonclient.NewWaiter(act, opt.Waiter)
|
||||
|
||||
return &Client{
|
||||
act: act,
|
||||
waiter: wtr,
|
||||
contract: contract,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewSimple creates a new Client using existing actor.Actor and default waiter options.
|
||||
// NewSimple creates a new Client using exising actor.Actor.
|
||||
func NewSimple(act *actor.Actor, contract util.Uint160) *Client {
|
||||
wtr := commonclient.NewWaiter(act, commonclient.WaiterOptions{})
|
||||
return &Client{
|
||||
act: act,
|
||||
waiter: wtr,
|
||||
contract: contract,
|
||||
}
|
||||
}
|
||||
|
@ -267,11 +260,6 @@ func (c Client) GetSubjectExtended(addr util.Uint160) (*SubjectExtended, error)
|
|||
return ParseSubjectExtended(items)
|
||||
}
|
||||
|
||||
// GetSubjectKV invokes `getSubjectKV` method of contract.
|
||||
func (c Client) GetSubjectKV(addr util.Uint160, name string) (string, error) {
|
||||
return unwrap.UTF8String(c.act.Call(c.contract, getSubjectKVMethod, addr, name))
|
||||
}
|
||||
|
||||
// ListSubjects gets all subjects.
|
||||
func (c Client) ListSubjects() ([]util.Uint160, error) {
|
||||
return UnwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listSubjectsMethod))
|
||||
|
@ -613,14 +601,18 @@ func (c Client) ListNonEmptyNamespaces() ([]string, error) {
|
|||
return res, nil
|
||||
}
|
||||
|
||||
// Wait waits until the specified transaction is accepted to the chain.
|
||||
// Wait invokes underlying wait method on actor.Actor.
|
||||
// Notice that "already exists" err value is treated as an error by this routine unlike actor.Waiter.
|
||||
func (c Client) Wait(tx util.Uint256, vub uint32, err error) (*state.AppExecResult, error) {
|
||||
return c.Waiter().Wait(tx, vub, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.act.Wait(tx, vub, err)
|
||||
}
|
||||
|
||||
// Waiter returns underlying waiter.Waiter.
|
||||
func (c Client) Waiter() waiter.Waiter {
|
||||
return c.waiter
|
||||
return c.act
|
||||
}
|
||||
|
||||
// ParseGroupID fetch groupID from stack after creating group method invocation.
|
||||
|
|
|
@ -7,7 +7,6 @@ safemethods:
|
|||
- "getGroupByName"
|
||||
- "getNamespace"
|
||||
- "getNamespaceExtended"
|
||||
- "getSubjectKV"
|
||||
- "getSubject"
|
||||
- "getSubjectExtended"
|
||||
- "getSubjectByKey"
|
||||
|
|
|
@ -20,7 +20,6 @@ FrostFSID contract does not produce notifications to process.
|
|||
| `G` + [ RIPEMD160 of namespace ] + [ 8 byte group id ] + [ subject address ] | []byte{1} | subject that belongs to the group |
|
||||
| `c` | Int | group id counter |
|
||||
| `m` + [ RIPEMD160 of namespace ] + [ RIPEMD160 of subject name ] | Serialized group id int | group name to group id index |
|
||||
| `A` + [ subject address ] | bool | means that the wallet has been used |
|
||||
|
||||
|
||||
*/
|
||||
|
|
|
@ -96,7 +96,6 @@ const (
|
|||
groupSubjectsKeysPrefix = 'G'
|
||||
groupCounterKey = 'c'
|
||||
namespaceGroupsNamesPrefix = 'm'
|
||||
addressPrefix = 'A'
|
||||
)
|
||||
|
||||
func _deploy(data any, isUpdate bool) {
|
||||
|
@ -113,27 +112,6 @@ func _deploy(data any, isUpdate bool) {
|
|||
storage.Put(ctx, adminKey, args.admin)
|
||||
}
|
||||
|
||||
if isUpdate {
|
||||
it := storage.Find(ctx, subjectKeysPrefix, storage.ValuesOnly)
|
||||
for iterator.Next(it) {
|
||||
subjectRaw := iterator.Value(it)
|
||||
subject := std.Deserialize(subjectRaw.([]byte)).(Subject)
|
||||
address := addressKey(contract.CreateStandardAccount(subject.PrimaryKey))
|
||||
if storage.Get(ctx, address) != nil {
|
||||
panic("frostfsid contract contains duplicate keys")
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
storage.Put(ctx, groupCounterKey, 0)
|
||||
storage.Put(ctx, namespaceKey(""), std.Serialize(Namespace{}))
|
||||
|
||||
|
@ -204,11 +182,6 @@ func CreateSubject(ns string, key interop.PublicKey) {
|
|||
panic("key is occupied")
|
||||
}
|
||||
|
||||
allAddressKey := addressKey(addr)
|
||||
if storage.Get(ctx, allAddressKey) != nil {
|
||||
panic("key is occupied by another additional key")
|
||||
}
|
||||
|
||||
nsKey := namespaceKey(ns)
|
||||
data = storage.Get(ctx, nsKey).([]byte)
|
||||
if data == nil {
|
||||
|
@ -224,7 +197,6 @@ func CreateSubject(ns string, key interop.PublicKey) {
|
|||
|
||||
nsSubjKey := namespaceSubjectKey(ns, addr)
|
||||
storage.Put(ctx, nsSubjKey, []byte{1})
|
||||
storage.Put(ctx, allAddressKey, true)
|
||||
|
||||
runtime.Notify("CreateSubject", interop.Hash160(addr))
|
||||
}
|
||||
|
@ -241,11 +213,6 @@ func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
|||
panic("incorrect public key length")
|
||||
}
|
||||
|
||||
addressKey := addressKey(contract.CreateStandardAccount(key))
|
||||
if storage.Get(ctx, addressKey) != nil {
|
||||
panic("key is occupied")
|
||||
}
|
||||
|
||||
saKey := subjectAdditionalKey(key, addr)
|
||||
data := storage.Get(ctx, saKey).([]byte)
|
||||
if data != nil {
|
||||
|
@ -263,7 +230,6 @@ func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
|||
subject.AdditionalKeys = append(subject.AdditionalKeys, key)
|
||||
|
||||
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||
storage.Put(ctx, addressKey, true)
|
||||
runtime.Notify("AddSubjectKey", addr, key)
|
||||
}
|
||||
|
||||
|
@ -303,7 +269,6 @@ func RemoveSubjectKey(addr interop.Hash160, key interop.PublicKey) {
|
|||
subject.AdditionalKeys = additionalKeys
|
||||
|
||||
storage.Put(ctx, sKey, std.Serialize(subject))
|
||||
storage.Delete(ctx, addressKey(contract.CreateStandardAccount(key)))
|
||||
runtime.Notify("RemoveSubjectKey", addr, key)
|
||||
}
|
||||
|
||||
|
@ -397,7 +362,6 @@ func DeleteSubject(addr interop.Hash160) {
|
|||
|
||||
for i := 0; i < len(subj.AdditionalKeys); i++ {
|
||||
storage.Delete(ctx, subjectAdditionalKey(subj.AdditionalKeys[i], addr))
|
||||
storage.Delete(ctx, addressKey(contract.CreateStandardAccount(subj.AdditionalKeys[i])))
|
||||
}
|
||||
storage.Delete(ctx, sKey)
|
||||
|
||||
|
@ -417,12 +381,7 @@ func GetSubject(addr interop.Hash160) Subject {
|
|||
sKey := subjectKeyFromAddr(addr)
|
||||
data := storage.Get(ctx, sKey).([]byte)
|
||||
if data == nil {
|
||||
a := getPrimaryAddr(ctx, addr)
|
||||
sKey = subjectKeyFromAddr(a)
|
||||
data = storage.Get(ctx, sKey).([]byte)
|
||||
if data == nil {
|
||||
panic("subject not found")
|
||||
}
|
||||
panic("subject not found")
|
||||
}
|
||||
|
||||
return std.Deserialize(data).(Subject)
|
||||
|
@ -474,22 +433,18 @@ func GetSubjectByKey(key interop.PublicKey) Subject {
|
|||
return std.Deserialize(data).(Subject)
|
||||
}
|
||||
|
||||
addr := getPrimaryAddr(ctx, contract.CreateStandardAccount(key))
|
||||
sKey = subjectKeyFromAddr(addr)
|
||||
data = storage.Get(ctx, sKey).([]byte)
|
||||
if data != nil {
|
||||
return std.Deserialize(data).(Subject)
|
||||
}
|
||||
|
||||
panic("subject not found")
|
||||
}
|
||||
|
||||
func getPrimaryAddr(ctx storage.Context, addr interop.Hash160) interop.Hash160 {
|
||||
saPrefix := append([]byte{additionalKeysPrefix}, addr...)
|
||||
saPrefix := subjectAdditionalPrefix(key)
|
||||
it := storage.Find(ctx, saPrefix, storage.KeysOnly|storage.RemovePrefix)
|
||||
if iterator.Next(it) {
|
||||
return iterator.Value(it).([]byte)
|
||||
for iterator.Next(it) {
|
||||
addr := iterator.Value(it).([]byte)
|
||||
sKey = subjectKeyFromAddr(addr)
|
||||
data = storage.Get(ctx, sKey).([]byte)
|
||||
if data != nil {
|
||||
return std.Deserialize(data).(Subject)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
panic("subject not found")
|
||||
}
|
||||
|
||||
|
@ -517,34 +472,6 @@ func GetSubjectKeyByName(ns, name string) interop.PublicKey {
|
|||
return subjKey
|
||||
}
|
||||
|
||||
// GetSubjectKV GetSubjectKey returns the value associated with the key for the subject.
|
||||
func GetSubjectKV(addr interop.Hash160, name string) string {
|
||||
if len(addr) != interop.Hash160Len {
|
||||
panic("incorrect address length")
|
||||
}
|
||||
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
sKey := subjectKeyFromAddr(addr)
|
||||
data := storage.Get(ctx, sKey).([]byte)
|
||||
if data == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
sbj := std.Deserialize(data).(Subject)
|
||||
|
||||
if sbj.KV == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
for k, v := range sbj.KV {
|
||||
if k == name {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func ListSubjects() iterator.Iterator {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
return storage.Find(ctx, []byte{subjectKeysPrefix}, storage.KeysOnly|storage.RemovePrefix)
|
||||
|
@ -1074,7 +1001,3 @@ func idToBytes(itemID int) []byte {
|
|||
zeros := make([]byte, 8-ln)
|
||||
return append(b, zeros...)
|
||||
}
|
||||
|
||||
func addressKey(address []byte) []byte {
|
||||
return append([]byte{addressPrefix}, address...)
|
||||
}
|
||||
|
|
3
go.mod
3
go.mod
|
@ -1,6 +1,6 @@
|
|||
module git.frostfs.info/TrueCloudLab/frostfs-contract
|
||||
|
||||
go 1.22
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.6.0
|
||||
|
@ -36,7 +36,6 @@ require (
|
|||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect
|
||||
github.com/twmb/murmur3 v1.1.8 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.2 // indirect
|
||||
|
|
22
go.sum
22
go.sum
|
@ -1,5 +1,4 @@
|
|||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h1:npHgfD4Tl2WJS3AJaMUi5ynGDPUBfkg3U3fCzDyXZ+4=
|
||||
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c=
|
||||
|
@ -7,7 +6,6 @@ github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a
|
|||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
|
||||
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
|
||||
github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc=
|
||||
|
@ -19,7 +17,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
|
||||
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
|
@ -31,14 +28,12 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
|||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
@ -50,13 +45,9 @@ github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU
|
|||
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
|
||||
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
|
||||
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
|
||||
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
|
||||
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
|
||||
|
@ -67,19 +58,15 @@ github.com/nspcc-dev/dbft v0.2.0/go.mod h1:oFE6paSC/yfFh9mcNU6MheMGOYXK9+sPiRk3Y
|
|||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU3v+zJcnHAVmHnZKt3I++tvn30gBj2rP2PocZMk=
|
||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc=
|
||||
github.com/nspcc-dev/hrw/v2 v2.0.1 h1:CxYUkBeJvNfMEn2lHhrV6FjY8pZPceSxXUtMVq0BUOU=
|
||||
github.com/nspcc-dev/hrw/v2 v2.0.1/go.mod h1:iZAs5hT2q47EGq6AZ0FjaUI6ggntOi7vrY4utfzk5VA=
|
||||
github.com/nspcc-dev/neo-go v0.106.3 h1:HEyhgkjQY+HfBzotMJ12xx2VuOUphkngZ4kEkjvXDtE=
|
||||
github.com/nspcc-dev/neo-go v0.106.3/go.mod h1:3vEwJ2ld12N7HRGCaH/l/7EwopplC/+8XdIdPDNmD/M=
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec h1:vDrbVXF2+2uP0RlkZmem3QYATcXCu9BzzGGCNsNcK7Q=
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 h1:arN0Ypn+jawZpu1BND7TGRn44InAVIqKygndsx0y2no=
|
||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4/go.mod h1:7Tm1NKEoUVVIUlkVwFrPh7GG5+Lmta2m7EGr4oVpBd8=
|
||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12 h1:mdxtlSU2I4oVZ/7AXTLKyz8uUPbDWikZw4DM8gvrddA=
|
||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12/go.mod h1:JdsEM1qgNukrWqgOBDChcYp8oY4XUzidcKaxY4hNJvQ=
|
||||
github.com/nspcc-dev/rfc6979 v0.2.1 h1:8wWxkamHWFmO790GsewSoKUSJjVnL1fmdRpokU/RgRM=
|
||||
github.com/nspcc-dev/rfc6979 v0.2.1/go.mod h1:Tk7h5kyUWkhjyO3zUgFFhy1v2vQv3BvQEntakdtqrWc=
|
||||
github.com/nspcc-dev/tzhash v1.7.2 h1:iRXoa9TJqH/DQO7FFcqpq9BdruF9E7/xnFGlIghl5J4=
|
||||
github.com/nspcc-dev/tzhash v1.7.2/go.mod h1:oHiH0qwmTsZkeVs7pvCS5cVXUaLhXxSFvnmnZ++ijm4=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
|
@ -105,8 +92,6 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
|
|||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
|
||||
|
@ -120,7 +105,6 @@ github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQut
|
|||
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
||||
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
|
@ -130,7 +114,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -141,7 +124,6 @@ golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
|||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -167,9 +149,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
|
||||
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
@ -180,14 +160,12 @@ google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGm
|
|||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
|
||||
|
|
|
@ -44,32 +44,3 @@ The committee makes new tokens (domains), sets, and charges a fee for issuance.
|
|||
## Globally Unique Domain Zone
|
||||
|
||||
For more information, see [here](../docs/globally-unique-domain-zone.md).
|
||||
|
||||
## NNS and Frostfsid
|
||||
|
||||
You can register a TLD domain without a committee signature using Frostfsid. To do this, create a new wallet
|
||||
```
|
||||
neo-go wallet init -w newwallet/wallet.json
|
||||
```
|
||||
|
||||
Get wallet address:
|
||||
```
|
||||
neo-go wallet dump-keys -w newwallet/wallet.json
|
||||
[subject-address]
|
||||
[subject-key]
|
||||
```
|
||||
|
||||
Create a subject in `FrostfsID`:
|
||||
```
|
||||
frostfs-adm morph frostfsid create-subject --subject-key="[subject-key]"
|
||||
```
|
||||
|
||||
Grant permissions to the wallet:
|
||||
```
|
||||
frostfs-adm morph nns give-privilege --subject-address="[subject-address]"
|
||||
```
|
||||
|
||||
Register domain:
|
||||
```
|
||||
neo-go contract invokefunction [NNS-hash] register "subdomain.domain" hash160:[subject-address] "email@frostfs.info" 10000 1000 1000 1000 -- [subject-address]:Global
|
||||
```
|
|
@ -2,7 +2,6 @@ name: "NameService"
|
|||
supportedstandards: ["NEP-11"]
|
||||
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf",
|
||||
"tokens", "properties", "roots", "getPrice", "isAvailable", "getRecords",
|
||||
"getAllRecords",
|
||||
"resolve", "version"]
|
||||
events:
|
||||
- name: RegisterDomain
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
package nns
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
)
|
||||
|
||||
const FrostfsIDNNSName = "frostfsid.frostfs"
|
||||
|
||||
const (
|
||||
FrostfsIDNNSTLDPermissionKey = "nns-allow-register-tld"
|
||||
FrostfsIDTLDRegistrationAllowed = "allow"
|
||||
)
|
||||
|
||||
func checkFrostfsID(ctx storage.Context, addr interop.Hash160) bool {
|
||||
if len(addr) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
frostfsIDAddress := getRecordsByType(ctx, []byte(tokenIDFromName(FrostfsIDNNSName)), FrostfsIDNNSName, TXT)
|
||||
if len(frostfsIDAddress) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
decodedBytes := std.Base58Decode([]byte(frostfsIDAddress[1]))
|
||||
|
||||
if len(decodedBytes) < 21 || management.GetContract(decodedBytes[1:21]) == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if res := contract.Call(decodedBytes[1:21], "getSubjectKV", contract.ReadOnly, addr, FrostfsIDNNSTLDPermissionKey).(string); res == FrostfsIDTLDRegistrationAllowed {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -41,14 +41,9 @@ 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 contains a flag indicating that this domain was created using GlobalDomain.
|
||||
//This is necessary to distinguish it from regular CNAME records.
|
||||
prefixGlobalDomain byte = 0x23
|
||||
// prefixCountSubDomains contains information about the number of domains in the zone.
|
||||
// If it is nil, it will definitely be calculated on the first removal.
|
||||
prefixCountSubDomains byte = 0x24
|
||||
// prefixAutoCreated contains a flag indicating whether the TLD domain was created automatically.
|
||||
prefixAutoCreated = 0x25
|
||||
)
|
||||
|
||||
// Values constraints.
|
||||
|
@ -79,7 +74,7 @@ const (
|
|||
|
||||
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.
|
||||
//It is guaranteed that two domains cannot point to the same global domain.
|
||||
Cnametgt = "cnametgt"
|
||||
)
|
||||
|
||||
|
@ -239,10 +234,13 @@ func IsAvailable(name string) bool {
|
|||
ctx := storage.GetReadOnlyContext()
|
||||
l := len(fragments)
|
||||
if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[l-1])...)) == nil {
|
||||
if l != 1 {
|
||||
panic("TLD not found")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
checkParent(ctx, fragments)
|
||||
checkParentExists(ctx, fragments)
|
||||
checkAvailableGlobalDomain(ctx, name)
|
||||
return storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...)) == nil
|
||||
}
|
||||
|
@ -306,27 +304,33 @@ func extractCnametgt(ctx storage.Context, name, domain string) string {
|
|||
return fragments[0] + "." + globalDomain
|
||||
}
|
||||
|
||||
// checkParent returns parent domain or empty string if domain not found.
|
||||
func checkParent(ctx storage.Context, fragments []string) string {
|
||||
// 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.
|
||||
// first denotes the deepest subdomain to check.
|
||||
func parentExpired(ctx storage.Context, fragments []string) string {
|
||||
now := int64(runtime.GetTime())
|
||||
last := len(fragments) - 1
|
||||
name := fragments[last]
|
||||
parent := ""
|
||||
for i := last; i > 0; i-- {
|
||||
if i != last {
|
||||
name = fragments[i] + "." + name
|
||||
}
|
||||
nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...))
|
||||
if nsBytes == nil {
|
||||
continue
|
||||
return name
|
||||
}
|
||||
ns := std.Deserialize(nsBytes.([]byte)).(NameState)
|
||||
if now >= ns.Expiration {
|
||||
panic("domain expired: " + name)
|
||||
return name
|
||||
}
|
||||
parent = name
|
||||
}
|
||||
return parent
|
||||
return ""
|
||||
}
|
||||
|
||||
// Register registers a new domain with the specified owner and name if it's available.
|
||||
|
@ -338,31 +342,24 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
|
|||
// 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)
|
||||
countZone := len(fragments)
|
||||
rootZone := []byte(fragments[countZone-1])
|
||||
tldKey := append([]byte{prefixRoot}, rootZone...)
|
||||
l := len(fragments)
|
||||
tldKey := append([]byte{prefixRoot}, []byte(fragments[l-1])...)
|
||||
|
||||
tldBytes := storage.Get(ctx, tldKey)
|
||||
if countZone == 1 {
|
||||
checkCommitteeAndFrostfsID(ctx, owner)
|
||||
if l == 1 {
|
||||
checkCommittee()
|
||||
if tldBytes != nil {
|
||||
panic("TLD already exists")
|
||||
}
|
||||
storage.Put(ctx, tldKey, 0)
|
||||
} else {
|
||||
parent := checkParent(ctx, fragments)
|
||||
if parent == "" {
|
||||
parent = fragments[len(fragments)-1]
|
||||
|
||||
storage.Put(ctx, append([]byte{prefixAutoCreated}, rootZone...), true)
|
||||
register(ctx, parent, owner, email, refresh, retry, expire, ttl)
|
||||
if tldBytes == nil {
|
||||
panic("TLD not found")
|
||||
}
|
||||
checkParentExists(ctx, fragments)
|
||||
|
||||
parentKey := getTokenKey([]byte(parent))
|
||||
parentKey := getTokenKey([]byte(name[len(fragments[0])+1:]))
|
||||
nsBytes := storage.Get(ctx, append([]byte{prefixName}, parentKey...))
|
||||
if nsBytes == nil {
|
||||
panic("parent does not exist:" + parent)
|
||||
}
|
||||
ns := std.Deserialize(nsBytes.([]byte)).(NameState)
|
||||
ns.checkAdmin()
|
||||
|
||||
|
@ -406,7 +403,6 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
|
|||
}
|
||||
checkAvailableGlobalDomain(ctx, name)
|
||||
|
||||
updateSubdDomainCounter(ctx, rootZone, countZone)
|
||||
putNameStateWithKey(ctx, tokenKey, ns)
|
||||
putSoaRecord(ctx, name, email, refresh, retry, expire, ttl)
|
||||
updateBalance(ctx, []byte(name), owner, +1)
|
||||
|
@ -581,23 +577,14 @@ func DeleteDomain(name string) {
|
|||
deleteDomain(ctx, name)
|
||||
}
|
||||
|
||||
func countSubdomains(name string) int {
|
||||
countSubDomains := 0
|
||||
func deleteDomain(ctx storage.Context, name string) {
|
||||
it := Tokens()
|
||||
for iterator.Next(it) {
|
||||
domain := iterator.Value(it)
|
||||
if std.MemorySearch([]byte(domain.(string)), []byte(name)) > 0 {
|
||||
countSubDomains = countSubDomains + 1
|
||||
panic("can't delete a domain that has subdomains")
|
||||
}
|
||||
}
|
||||
return countSubDomains
|
||||
}
|
||||
|
||||
func deleteDomain(ctx storage.Context, name string) {
|
||||
fragments := splitAndCheck(name)
|
||||
parent := []byte(fragments[len(fragments)-1])
|
||||
countSubDomainsKey := append([]byte{prefixCountSubDomains}, parent...)
|
||||
autoCreatedPrefix := append([]byte{prefixAutoCreated}, parent...)
|
||||
|
||||
nsKey := append([]byte{prefixName}, getTokenKey([]byte(name))...)
|
||||
nsRaw := storage.Get(ctx, nsKey)
|
||||
|
@ -608,21 +595,6 @@ func deleteDomain(ctx storage.Context, name string) {
|
|||
ns := std.Deserialize(nsRaw.([]byte)).(NameState)
|
||||
ns.checkAdmin()
|
||||
|
||||
countSubDomain := 0
|
||||
countSubDomainRaw := storage.Get(ctx, countSubDomainsKey)
|
||||
if countSubDomainRaw != nil {
|
||||
countSubDomain = common.FromFixedWidth64(countSubDomainRaw.([]byte))
|
||||
} else {
|
||||
countSubDomain = countSubdomains(fragments[len(fragments)-1])
|
||||
}
|
||||
|
||||
if countSubDomain > 1 && len(fragments) == 1 {
|
||||
panic("can't delete TLD domain that has subdomains")
|
||||
}
|
||||
|
||||
countSubDomain = countSubDomain - 1
|
||||
storage.Put(ctx, countSubDomainsKey, common.ToFixedWidth64(countSubDomain))
|
||||
|
||||
globalNSKey := append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...)
|
||||
globalDomainRaw := storage.Get(ctx, globalNSKey)
|
||||
globalDomain := globalDomainRaw.(string)
|
||||
|
@ -636,17 +608,6 @@ func deleteDomain(ctx storage.Context, name string) {
|
|||
deleteRecords(ctx, name, AAAA)
|
||||
storage.Delete(ctx, nsKey)
|
||||
storage.Delete(ctx, append([]byte{prefixRoot}, []byte(name)...))
|
||||
|
||||
isAutoCreated := storage.Get(ctx, autoCreatedPrefix)
|
||||
if countSubDomain == 1 && isAutoCreated != nil && isAutoCreated.(bool) {
|
||||
deleteDomain(ctx, fragments[len(fragments)-1])
|
||||
}
|
||||
|
||||
if len(fragments) == 1 {
|
||||
storage.Delete(ctx, countSubDomainsKey)
|
||||
storage.Delete(ctx, autoCreatedPrefix)
|
||||
}
|
||||
|
||||
runtime.Notify("DeleteDomain", name)
|
||||
}
|
||||
|
||||
|
@ -720,7 +681,7 @@ func getNameState(ctx storage.Context, tokenID []byte) NameState {
|
|||
tokenKey := getTokenKey(tokenID)
|
||||
ns := getNameStateWithKey(ctx, tokenKey)
|
||||
fragments := std.StringSplit(string(tokenID), ".")
|
||||
checkParent(ctx, fragments)
|
||||
checkParentExists(ctx, fragments)
|
||||
return ns
|
||||
}
|
||||
|
||||
|
@ -924,14 +885,6 @@ func isValid(address interop.Hash160) bool {
|
|||
return address != nil && len(address) == interop.Hash160Len
|
||||
}
|
||||
|
||||
// checkCommitteeAndFrostfsID panics if the script container is not signed by the committee.
|
||||
// or if the owner does not have permission in FrostfsID.
|
||||
func checkCommitteeAndFrostfsID(ctx storage.Context, owner interop.Hash160) {
|
||||
if !checkFrostfsID(ctx, owner) {
|
||||
checkCommittee()
|
||||
}
|
||||
}
|
||||
|
||||
// checkCommittee panics if the script container is not signed by the committee.
|
||||
func checkCommittee() {
|
||||
committee := neo.GetCommittee()
|
||||
|
@ -1174,16 +1127,3 @@ func getAllRecords(ctx storage.Context, name string) iterator.Iterator {
|
|||
recordsKey := getRecordsKey(tokenID, name)
|
||||
return storage.Find(ctx, recordsKey, storage.ValuesOnly|storage.DeserializeValues)
|
||||
}
|
||||
|
||||
func updateSubdDomainCounter(ctx storage.Context, rootZone []byte, countZone int) {
|
||||
countSubDomain := 0
|
||||
delInfoRaw := storage.Get(ctx, append([]byte{prefixCountSubDomains}, rootZone...))
|
||||
if delInfoRaw != nil {
|
||||
countSubDomain = common.FromFixedWidth64(delInfoRaw.([]byte))
|
||||
}
|
||||
|
||||
if delInfoRaw != nil || countZone == 1 {
|
||||
countSubDomain = countSubDomain + 1
|
||||
storage.Put(ctx, append([]byte{prefixCountSubDomains}, rootZone...), common.ToFixedWidth64(countSubDomain))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,11 +222,6 @@ func (c *ContractReader) GetSubjectExtended(addr util.Uint160) ([]stackitem.Item
|
|||
return unwrap.Array(c.invoker.Call(c.hash, "getSubjectExtended", addr))
|
||||
}
|
||||
|
||||
// GetSubjectKV invokes `getSubjectKV` method of contract.
|
||||
func (c *ContractReader) GetSubjectKV(addr util.Uint160, name string) (string, error) {
|
||||
return unwrap.UTF8String(c.invoker.Call(c.hash, "getSubjectKV", addr, name))
|
||||
}
|
||||
|
||||
// GetSubjectKeyByName invokes `getSubjectKeyByName` method of contract.
|
||||
func (c *ContractReader) GetSubjectKeyByName(ns string, name string) (*keys.PublicKey, error) {
|
||||
return unwrap.PublicKey(c.invoker.Call(c.hash, "getSubjectKeyByName", ns, name))
|
||||
|
|
|
@ -91,20 +91,6 @@ func New(actor Actor, hash util.Uint160) *Contract {
|
|||
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.
|
||||
func (c *ContractReader) GetPrice() (*big.Int, error) {
|
||||
return unwrap.BigInt(c.invoker.Call(c.hash, "getPrice"))
|
||||
|
@ -248,6 +234,28 @@ func (c *Contract) DeleteRecordsUnsigned(name string, typ *big.Int) (*transactio
|
|||
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) {
|
||||
return smartcontract.CreateCallWithAssertScript(c.hash, "register", name, owner, email, refresh, retry, expire, ttl)
|
||||
}
|
||||
|
|
|
@ -238,13 +238,6 @@ func TestFrostFSID_SubjectManagement(t *testing.T) {
|
|||
subj = parseSubject(t, s)
|
||||
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) {
|
||||
anonInvoker.InvokeFail(t, notWitnessedError, removeSubjectKeyMethod, subjKeyAddr, subjNewKey.PublicKey().Bytes())
|
||||
invoker.Invoke(t, stackitem.Null{}, removeSubjectKeyMethod, subjKeyAddr, subjNewKey.PublicKey().Bytes())
|
||||
|
@ -611,44 +604,6 @@ func TestFrostFSID_GroupManagement(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAdditionalKeyFromPrimarySubject(t *testing.T) {
|
||||
f := newFrostFSIDInvoker(t)
|
||||
invoker := f.OwnerInvoker()
|
||||
|
||||
subjAPrimaryKey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
subjAKeyAddr := subjAPrimaryKey.PublicKey().GetScriptHash()
|
||||
|
||||
subjBPrimaryKey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
subjBKeyAddr := subjBPrimaryKey.PublicKey().GetScriptHash()
|
||||
|
||||
subjCPrimaryKey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
subjDPrimaryKey, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
invoker.Invoke(t, stackitem.Null{}, createSubjectMethod, defaultNamespace, subjAPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.Invoke(t, stackitem.Null{}, createSubjectMethod, defaultNamespace, subjBPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.InvokeFail(t, "key is occupied", addSubjectKeyMethod, subjBKeyAddr, subjAPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.Invoke(t, stackitem.Null{}, addSubjectKeyMethod, subjAKeyAddr, subjCPrimaryKey.PublicKey().Bytes())
|
||||
invoker.InvokeFail(t, "key is occupied", addSubjectKeyMethod, subjBKeyAddr, subjCPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.Invoke(t, stackitem.Null{}, addSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||
|
||||
invoker.InvokeFail(t, "key is occupied", addSubjectKeyMethod, subjBKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||
invoker.Invoke(t, stackitem.Null{}, removeSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||
invoker.Invoke(t, stackitem.Null{}, addSubjectKeyMethod, subjBKeyAddr, 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{}, addSubjectKeyMethod, subjAKeyAddr, subjDPrimaryKey.PublicKey().Bytes())
|
||||
}
|
||||
|
||||
func checkPublicKeyResult(t *testing.T, s *vm.Stack, err error, key *keys.PrivateKey) {
|
||||
if key == nil {
|
||||
require.ErrorContains(t, err, "not found")
|
||||
|
|
|
@ -12,12 +12,10 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
|
||||
"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/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -25,7 +23,8 @@ const nnsPath = "../nns"
|
|||
|
||||
const msPerYear = 365 * 24 * time.Hour / time.Millisecond
|
||||
|
||||
func deployNNS(t *testing.T, e *neotest.Executor, addRoot bool) *neotest.ContractInvoker {
|
||||
func newNNSInvoker(t *testing.T, addRoot bool) *neotest.ContractInvoker {
|
||||
e := newExecutor(t)
|
||||
ctr := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
|
||||
e.DeployContract(t, ctr, nil)
|
||||
|
||||
|
@ -40,28 +39,6 @@ func deployNNS(t *testing.T, e *neotest.Executor, addRoot bool) *neotest.Contrac
|
|||
return c
|
||||
}
|
||||
|
||||
func newNNSInvoker(t *testing.T, addRoot bool) *neotest.ContractInvoker {
|
||||
e := newExecutor(t)
|
||||
return deployNNS(t, e, addRoot)
|
||||
}
|
||||
|
||||
func newNNSInvokerWithFrostfsID(t *testing.T, addRoot bool) (*neotest.ContractInvoker, *neotest.ContractInvoker) {
|
||||
e := newExecutor(t)
|
||||
c := deployNNS(t, e, addRoot)
|
||||
|
||||
frostfdID := deployFrostFSIDContract(t, e, e.CommitteeHash)
|
||||
c.Invoke(t, true, "register",
|
||||
nns.FrostfsIDNNSName, c.CommitteeHash,
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
c.Invoke(t, stackitem.Null{}, "addRecord",
|
||||
nns.FrostfsIDNNSName, int64(nns.TXT), frostfdID.StringLE())
|
||||
|
||||
c.Invoke(t, stackitem.Null{}, "addRecord",
|
||||
nns.FrostfsIDNNSName, int64(nns.TXT), address.Uint160ToString(frostfdID))
|
||||
return e.NewInvoker(c.Hash), e.CommitteeInvoker(frostfdID)
|
||||
}
|
||||
|
||||
func TestNNSGeneric(t *testing.T) {
|
||||
c := newNNSInvoker(t, false)
|
||||
|
||||
|
@ -123,18 +100,6 @@ func TestNNSRegister(t *testing.T) {
|
|||
"com", accTop.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"aa.bb.zz", accTop.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c1.InvokeFail(t, "TLD already exists", "register",
|
||||
"zz", accTop.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"xx.bb.zz", accTop.ScriptHash(),
|
||||
"myemail@frostfs.info", refresh, retry, expire, ttl)
|
||||
|
||||
acc := c.NewAccount(t)
|
||||
c2 := c.WithSigners(c.Committee, acc)
|
||||
c2.InvokeFail(t, "not witnessed by admin", "register",
|
||||
|
@ -177,10 +142,8 @@ func TestNNSRegister(t *testing.T) {
|
|||
|
||||
cAcc := c.WithSigners(acc)
|
||||
|
||||
expected = stackitem.NewArray([]stackitem.Item{
|
||||
stackitem.NewByteArray([]byte("testdomain.com")),
|
||||
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT))),
|
||||
})
|
||||
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("testdomain.com")),
|
||||
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT)))})
|
||||
tx = cAcc.Invoke(t, stackitem.Null{}, "addRecord",
|
||||
"testdomain.com", int64(nns.TXT), "first TXT record")
|
||||
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "AddRecord", Item: expected})
|
||||
|
@ -196,10 +159,8 @@ func TestNNSRegister(t *testing.T) {
|
|||
})
|
||||
c.Invoke(t, expected, "getRecords", "testdomain.com", int64(nns.TXT))
|
||||
|
||||
expected = stackitem.NewArray([]stackitem.Item{
|
||||
stackitem.NewByteArray([]byte("testdomain.com")),
|
||||
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT))),
|
||||
})
|
||||
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("testdomain.com")),
|
||||
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT)))})
|
||||
tx = cAcc.Invoke(t, stackitem.Null{}, "setRecord",
|
||||
"testdomain.com", int64(nns.TXT), int64(0), "replaced first")
|
||||
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "AddRecord", Item: expected})
|
||||
|
@ -211,10 +172,8 @@ func TestNNSRegister(t *testing.T) {
|
|||
c.Invoke(t, expected, "getRecords", "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")),
|
||||
stackitem.NewBigInteger(big.NewInt(int64(nns.TXT))),
|
||||
})
|
||||
expected = stackitem.NewArray([]stackitem.Item{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.Invoke(t, stackitem.Null{}, "getRecords", "testdomain.com", int64(nns.TXT))
|
||||
|
@ -261,10 +220,8 @@ func TestNNSRegister(t *testing.T) {
|
|||
c.Invoke(t, stackitem.Null{}, "getRecords", "testdomain.com", int64(nns.TXT))
|
||||
|
||||
tx = cAcc.Invoke(t, stackitem.Null{}, "deleteDomain", "testdomain.com")
|
||||
expected = stackitem.NewArray([]stackitem.Item{
|
||||
stackitem.NewByteArray([]byte("testdomain.com")),
|
||||
stackitem.NewBigInteger(big.NewInt(int64(nns.CNAME))),
|
||||
})
|
||||
expected = stackitem.NewArray([]stackitem.Item{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})
|
||||
|
||||
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("testdomain.com"))})
|
||||
|
@ -294,23 +251,10 @@ func TestDeleteDomain(t *testing.T) {
|
|||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
c1.InvokeFail(t, "domain not found", "deleteDomain", "ru")
|
||||
c1.InvokeFail(t, "can't delete TLD domain that has subdomains", "deleteDomain", "com")
|
||||
c1.InvokeFail(t, "can't delete a domain that has subdomains", "deleteDomain", "testdomain.com")
|
||||
c1.Invoke(t, stackitem.Null{}, "deleteDomain", "domik.testdomain.com")
|
||||
c1.Invoke(t, stackitem.Null{}, "deleteDomain", "testdomain.com")
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"aa.bb.zz", acc1.ScriptHash(),
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
c1.InvokeFail(t, "TLD already exists", "register",
|
||||
"zz", acc1.ScriptHash(),
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
c1.Invoke(t, stackitem.Null{}, "deleteDomain", "aa.bb.zz")
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"zz", acc1.ScriptHash(),
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"cn", acc1.ScriptHash(),
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
@ -322,18 +266,6 @@ func TestDeleteDomain(t *testing.T) {
|
|||
c2.Invoke(t, true, "register",
|
||||
"cn", acc2.ScriptHash(),
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"gu.bububu.bubu.bu", c.CommitteeHash,
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"buu.gu.bububu.bubu.bu", c.CommitteeHash,
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"bubu.bu", c.CommitteeHash,
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
}
|
||||
|
||||
func TestGlobalDomain(t *testing.T) {
|
||||
|
@ -379,7 +311,6 @@ func TestGlobalDomain(t *testing.T) {
|
|||
|
||||
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",
|
||||
|
@ -404,6 +335,11 @@ func TestNNSRegisterMulti(t *testing.T) {
|
|||
cBoth.Invoke(t, true, "register", args...)
|
||||
|
||||
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"
|
||||
args[0] = "testnet.fs.neo.com"
|
||||
c1.InvokeFail(t, msg, "register", args...)
|
||||
})
|
||||
|
||||
args[0] = "fs.neo.com"
|
||||
c1.Invoke(t, true, "register", args...)
|
||||
|
@ -559,44 +495,11 @@ func TestNNSSetAdmin(t *testing.T) {
|
|||
"testdomain.com", int64(nns.TXT), "will be added")
|
||||
}
|
||||
|
||||
func TestNNS_Frostfsid(t *testing.T) {
|
||||
nnsInv, f := newNNSInvokerWithFrostfsID(t, false)
|
||||
|
||||
acc, err := wallet.NewAccount()
|
||||
require.NoError(t, err)
|
||||
nnsUserInv := nnsInv.NewInvoker(nnsInv.Hash, newSigner(t, nnsInv, acc))
|
||||
|
||||
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
|
||||
"ddd", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
|
||||
"testdomain.kz", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
f.Invoke(t, stackitem.Null{}, createSubjectMethod, "", acc.PrivateKey().PublicKey().Bytes())
|
||||
|
||||
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
|
||||
"testdomain.kz", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
f.Invoke(t, stackitem.Null{}, setSubjectKVMethod, acc.ScriptHash(), nns.FrostfsIDNNSTLDPermissionKey, nns.FrostfsIDTLDRegistrationAllowed)
|
||||
|
||||
nnsUserInv.Invoke(t, true, "register",
|
||||
"testdomain.kz", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
|
||||
f.Invoke(t, stackitem.Null{}, deleteSubjectKVMethod, acc.ScriptHash(), nns.FrostfsIDNNSTLDPermissionKey)
|
||||
|
||||
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
|
||||
"testdomain.uz", acc.ScriptHash(),
|
||||
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
|
||||
}
|
||||
|
||||
func TestNNSIsAvailable(t *testing.T) {
|
||||
c := newNNSInvoker(t, false)
|
||||
|
||||
c.Invoke(t, true, "isAvailable", "com")
|
||||
c.InvokeFail(t, "TLD not found", "isAvailable", "domain.com")
|
||||
|
||||
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
|
||||
c.Invoke(t, true, "register",
|
||||
|
@ -609,6 +512,7 @@ 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)
|
||||
|
@ -620,6 +524,7 @@ func TestNNSIsAvailable(t *testing.T) {
|
|||
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")
|
||||
|
||||
c1.Invoke(t, true, "register",
|
||||
"dom.domain.com", acc.ScriptHash(),
|
||||
|
|
Loading…
Reference in a new issue