Compare commits

...

27 commits

Author SHA1 Message Date
3e221b973a [#113] Regenerate wrappers
All checks were successful
DCO action / DCO (pull_request) Successful in 28s
Code generation / Generate wrappers (pull_request) Successful in 1m3s
Tests / Tests (pull_request) Successful in 1m11s
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-09-09 10:27:09 +03:00
d36120014d [#113] .forgejo: Add generate-wrappers action
Make it easier to maintain auto-generated code in the actual state.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-09-09 10:26:56 +03:00
2a1bf77d74 [#113] go.mod: Update neo-go
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-09-09 09:35:31 +03:00
82641e2e99 [#109] nns: Add notification sending
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-09-09 06:24:55 +00:00
6e244fac04 [#110] nns: Remove unused config
All checks were successful
DCO action / DCO (pull_request) Successful in 41s
Tests / Tests (pull_request) Successful in 1m6s
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-09-06 12:06:19 +03:00
c142971bfd [#107] Add client method ListFullSubjects
All checks were successful
Tests / Tests (1.22) (pull_request) Successful in 1m3s
Tests / Tests (1.21) (pull_request) Successful in 1m8s
DCO action / DCO (pull_request) Successful in 29s
Signed-off-by: d.zverev <d.zverev@yadro.com>
2024-08-27 13:18:00 +00:00
cee957e85a [#105] policy: Add ListChainNames method
All checks were successful
DCO action / DCO (pull_request) Successful in 43s
Tests / Tests (1.21) (pull_request) Successful in 1m13s
Tests / Tests (1.22) (pull_request) Successful in 1m14s
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2024-08-27 12:03:05 +03:00
0befe361fe [#104] rpcclient: Regenerate wrappers
All checks were successful
DCO action / DCO (pull_request) Successful in 54s
Tests / Tests (1.21) (pull_request) Successful in 1m22s
Tests / Tests (1.22) (pull_request) Successful in 1m20s
Introduced in #102.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-08-19 18:08:51 +03:00
ba7329c3a7 [#103] common: Disallow downgrading contracts
All checks were successful
DCO action / DCO (pull_request) Successful in 37s
Tests / Tests (1.21) (pull_request) Successful in 1m11s
Tests / Tests (1.22) (pull_request) Successful in 1m11s
`PrevVersion` marks suitable version that we can upgrade from.
However, we can have multiple minor versions, so, currently an upgrade
from v0.19.3 to v0.19.1 is possible. Prevent this with an additional
check.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-08-19 13:57:03 +03:00
82e04b6c32 [#102] nns: Support global domain
All checks were successful
DCO action / DCO (pull_request) Successful in 45s
Tests / Tests (1.21) (pull_request) Successful in 1m9s
Tests / Tests (1.22) (pull_request) Successful in 1m9s
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-08-16 16:38:54 +03:00
b2eb585bb6 [#102] nns: Support global domain
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-08-16 16:37:17 +03:00
49e5270f67 [#92] nns: Mention domain in panic messages
All checks were successful
DCO action / DCO (pull_request) Successful in 1m1s
Tests / Tests (1.22) (pull_request) Successful in 1m28s
Tests / Tests (1.21) (pull_request) Successful in 1m39s
Signed-off-by: Ekaterina Lebedeva <ekaterina.lebedeva@yadro.com>
2024-06-21 16:12:49 +03:00
2aba66806c [#93] frostfsid: Move struct parsers to separate file
All checks were successful
DCO action / DCO (pull_request) Successful in 1m2s
Tests / Tests (1.21) (pull_request) Successful in 1m27s
Tests / Tests (1.22) (pull_request) Successful in 1m28s
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-06-21 14:33:04 +03:00
ba4ef7bd22 [#89] frostfsid: Add description for FrostFS ID
All checks were successful
DCO action / DCO (pull_request) Successful in 1m10s
Tests / Tests (1.22) (pull_request) Successful in 1m31s
Tests / Tests (1.21) (pull_request) Successful in 1m39s
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
2024-05-24 21:32:20 +03:00
db36131800 [#90] policy: Add constants for more targets
All checks were successful
DCO action / DCO (pull_request) Successful in 1m15s
Tests / Tests (1.21) (pull_request) Successful in 1m48s
Tests / Tests (1.22) (pull_request) Successful in 1m48s
Signed-off-by: Alex Vanin <a.vanin@yadro.com>
2024-04-17 11:01:07 +03:00
a3d5e02f20 [#81] Add domain in the error message in NNS
All checks were successful
DCO action / DCO (pull_request) Successful in 54s
Tests / Tests (1.22) (pull_request) Successful in 1m25s
Tests / Tests (1.21) (pull_request) Successful in 1m31s
Signed-off-by: Ekaterina Lebedeva <ekaterina.lebedeva@yadro.com>
2024-04-16 18:05:46 +03:00
6eb492025b [#86] .forgejo: Update go version
All checks were successful
DCO action / DCO (pull_request) Successful in 1m11s
Tests / Tests (1.21) (pull_request) Successful in 1m45s
Tests / Tests (1.22) (pull_request) Successful in 2m2s
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-04-09 14:57:29 +03:00
1addbfef2d [#86] .forgejo: Update DCO action version
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-04-09 14:57:29 +03:00
e7a05a49ff [#XX] client: Terminate session in ReadIteratorItems
All checks were successful
DCO action / DCO (pull_request) Successful in 1m10s
Tests / Tests (1.19) (pull_request) Successful in 1m38s
Tests / Tests (1.20) (pull_request) Successful in 1m34s
* Make an invoker terminate session by its ID before return, otherwise,
  it may lead to `max session capacity reached error`.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-04-09 14:15:39 +03:00
694daebb19 [#84] policy: Fix IteratorChainsByPrefix method
All checks were successful
DCO action / DCO (pull_request) Successful in 1m11s
Tests / Tests (1.20) (pull_request) Successful in 1m24s
Tests / Tests (1.19) (pull_request) Successful in 1m37s
* If numeric mapping does not exists, then assign id to 0.
* Add check to unit-test.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-04-04 15:43:19 +03:00
2574b2840e [#84] rpcclient: Regenerate interface for Policy
Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-04-04 10:26:28 +03:00
42344eaa69 [#83] container: Remove outdated migration code
All checks were successful
DCO action / DCO (pull_request) Successful in 1m9s
Tests / Tests (1.20) (pull_request) Successful in 1m33s
Tests / Tests (1.19) (pull_request) Successful in 1m40s
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-03-14 10:03:23 +03:00
e27b8ad306 [#83] policy: Allow to update contract
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-03-14 10:03:23 +03:00
5119f655fe [#82] common: Update version
All checks were successful
DCO action / DCO (pull_request) Successful in 1m1s
Tests / Tests (1.20) (pull_request) Successful in 1m32s
Tests / Tests (1.19) (pull_request) Successful in 1m38s
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-03-13 20:57:09 +03:00
c9c53bb9ec [#17] frostfs: Remove method alphabetAddress
All checks were successful
DCO action / DCO (pull_request) Successful in 1m3s
Tests / Tests (1.19) (pull_request) Successful in 1m34s
Tests / Tests (1.20) (pull_request) Successful in 1m29s
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
2024-03-05 12:59:03 +03:00
43c90af97d [#78] policy: Fix counter key prefix name
All checks were successful
DCO action / DCO (pull_request) Successful in 4m32s
Tests / Tests (1.19) (pull_request) Successful in 4m42s
Tests / Tests (1.20) (pull_request) Successful in 4m44s
* Fix counterKey: "counter" may conflict with 'c' prefix.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-02-20 11:58:33 +03:00
d9f523ee07 [#78] policy: Introduce ListTargets method for Policy contract
* Introduce a new method ListTargets that lists targets by kind.
* Slightly fix key mapping - also concatenate kind to prefix.
* Write unit-tests.
* Regenerate rpcclient.

Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
2024-02-20 11:57:34 +03:00
29 changed files with 1475 additions and 1050 deletions

View file

@ -0,0 +1,22 @@
name: Code generation
on: [pull_request]
jobs:
wrappers:
name: Generate wrappers
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.23'
- name: Generate wrappers
run: make generate-wrappers
# The command seems to be non-deterministic.
# However, with >20 runs I haven't been able to reproduce the issue.
# This `git diff` is here to print diff in case we catch the behaviour again.
- name: Print diff
run: git diff HEAD
- name: Check that nothing has changed
run: git diff-index --exit-code HEAD

View file

@ -13,9 +13,9 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
- name: Run commit format checker
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v2
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
with:
from: 'origin/${{ github.event.pull_request.base.ref }}'

View file

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go_versions: [ '1.19', '1.20' ]
go_versions: [ '1.21', '1.22' ]
fail-fast: false
steps:
- uses: actions/checkout@v3

View file

@ -1 +1 @@
v0.18.0
v0.19.1

View file

@ -4,14 +4,14 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
const (
major = 0
minor = 18
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 = 16
prevMinor = 18
prevPatch = 0
Version = major*1_000_000 + minor*1_000 + patch
@ -32,7 +32,7 @@ func CheckVersion(from int) {
if from < PrevVersion {
panic(ErrVersionMismatch + ": expected >=" + std.Itoa(PrevVersion, 10))
}
if from == Version {
if Version <= from {
panic(ErrAlreadyUpdated + ": " + std.Itoa(Version, 10))
}
}

View file

@ -29,6 +29,10 @@ func ReadIteratorItems(inv Invoker, batchSize int, contract util.Uint160, method
return arr, nil
}
defer func() {
_ = inv.TerminateSession(sessionID)
}()
var shouldStop bool
res := arr
for !shouldStop {

View file

@ -94,26 +94,6 @@ func _deploy(data any, isUpdate bool) {
if isUpdate {
args := data.([]any)
common.CheckVersion(args[len(args)-1].(int))
it := storage.Find(ctx, []byte{}, storage.None)
for iterator.Next(it) {
item := iterator.Value(it).(struct {
key []byte
value []byte
})
// Migrate container.
if len(item.key) == containerIDSize {
storage.Delete(ctx, item.key)
storage.Put(ctx, append([]byte{containerKeyPrefix}, item.key...), item.value)
}
// Migrate owner-cid map.
if len(item.key) == 25 /* owner id size */ +containerIDSize {
storage.Delete(ctx, item.key)
storage.Put(ctx, append([]byte{ownerKeyPrefix}, item.key...), item.value)
}
}
return
}

View file

@ -1,6 +1,5 @@
name: "FrostFS"
safemethods:
- "alphabetAddress"
- "config"
- "innerRingCandidates"
- "listConfig"

View file

@ -9,7 +9,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
@ -26,7 +25,6 @@ const (
CandidateFeeConfigKey = "InnerRingCandidateFee"
withdrawFeeConfigKey = "WithdrawFee"
alphabetKey = "alphabet"
candidatesKey = "candidates"
processingContractKey = "processingScriptHash"
@ -71,9 +69,6 @@ func _deploy(data any, isUpdate bool) {
}
}
// initialize all storage slices
common.SetSerialized(ctx, alphabetKey, args.keys)
storage.Put(ctx, processingContractKey, args.addrProc)
ln := len(args.config)
@ -106,13 +101,6 @@ func Update(script []byte, manifest []byte, data any) {
runtime.Log("frostfs contract updated")
}
// AlphabetAddress returns 2\3n+1 multisignature address of alphabet nodes.
// It is used in sidechain notary disabled environment.
func AlphabetAddress() interop.Hash160 {
ctx := storage.GetReadOnlyContext()
return multiaddress(getAlphabetNodes(ctx))
}
// InnerRingCandidates returns an array of structures that contain an Inner Ring
// candidate node key.
func InnerRingCandidates() []common.IRNode {
@ -137,8 +125,7 @@ func InnerRingCandidateRemove(key interop.PublicKey) {
keyOwner := runtime.CheckWitness(key)
if !keyOwner {
multiaddr := AlphabetAddress()
if !runtime.CheckWitness(multiaddr) {
if !runtime.CheckWitness(common.AlphabetAddress()) {
panic("this method must be invoked by candidate or alphabet")
}
}
@ -358,16 +345,6 @@ func Version() int {
return common.Version
}
// getAlphabetNodes returns a deserialized slice of nodes from storage.
func getAlphabetNodes(ctx storage.Context) []interop.PublicKey {
data := storage.Get(ctx, alphabetKey)
if data != nil {
return std.Deserialize(data.([]byte)).([]interop.PublicKey)
}
return []interop.PublicKey{}
}
// getConfig returns the installed frostfs configuration value or nil if it is not set.
func getConfig(ctx storage.Context, key any) interface{} {
postfix := key.([]byte)

View file

@ -1,19 +1,20 @@
package client
import (
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-contract/commonclient"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/nspcc-dev/neo-go/pkg/wallet"
@ -246,7 +247,7 @@ func (c Client) GetSubject(addr util.Uint160) (*Subject, error) {
return nil, err
}
return parseSubject(items)
return ParseSubject(items)
}
// GetSubjectExtended gets extended subject by address.
@ -256,12 +257,45 @@ func (c Client) GetSubjectExtended(addr util.Uint160) (*SubjectExtended, error)
return nil, err
}
return parseSubjectExtended(items)
return ParseSubjectExtended(items)
}
// ListSubjects gets all subjects.
func (c Client) ListSubjects() ([]util.Uint160, error) {
return unwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listSubjectsMethod))
return UnwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listSubjectsMethod))
}
// ListFullSubjects gets list of subjects.
func (c Client) ListFullSubjects(hashes []util.Uint160) ([]*Subject, error) {
w := io.NewBufBinWriter()
for _, hash := range hashes {
emit.AppCall(w.BinWriter, c.contract, getSubjectMethod, callflag.All, hash)
}
invoker, err := c.act.Run(w.Bytes())
if err != nil {
return nil, err
}
if invoker.State != vmstate.Halt.String() {
return nil, fmt.Errorf("invocation failed: %s", invoker.FaultException)
}
subjects := make([]*Subject, 0, len(invoker.Stack))
for _, item := range invoker.Stack {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid subject")
}
subject, err := ParseSubject(arr)
if err != nil {
return nil, err
}
subjects = append(subjects, subject)
}
return subjects, nil
}
// AddSubjectKey adds extra public key to subject.
@ -332,7 +366,7 @@ func (c Client) GetSubjectByKey(key *keys.PublicKey) (*Subject, error) {
return nil, err
}
return parseSubject(items)
return ParseSubject(items)
}
// GetSubjectByName gets subject by its name (namespace scope).
@ -342,7 +376,7 @@ func (c Client) GetSubjectByName(namespace, subjectName string) (*Subject, error
return nil, err
}
return parseSubject(items)
return ParseSubject(items)
}
// GetSubjectKeyByName gets subject public key by its name (namespace scope).
@ -381,7 +415,7 @@ func (c Client) GetNamespace(namespace string) (*Namespace, error) {
return nil, err
}
return parseNamespace(items)
return ParseNamespace(items)
}
// GetNamespaceExtended gets extended namespace.
@ -391,7 +425,7 @@ func (c Client) GetNamespaceExtended(namespace string) (*NamespaceExtended, erro
return nil, err
}
return parseNamespaceExtended(items)
return ParseNamespaceExtended(items)
}
// ListNamespaces gets all namespaces.
@ -401,12 +435,12 @@ func (c Client) ListNamespaces() ([]*Namespace, error) {
return nil, err
}
return parseNamespaces(items)
return ParseNamespaces(items)
}
// ListNamespaceSubjects gets all subjects from namespace.
func (c Client) ListNamespaceSubjects(namespace string) ([]util.Uint160, error) {
return unwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listNamespaceSubjectsMethod, namespace))
return UnwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listNamespaceSubjectsMethod, namespace))
}
// CreateGroup creates a new group in specific namespace.
@ -428,7 +462,7 @@ func (c Client) GetGroup(namespace string, groupID int64) (*Group, error) {
return nil, err
}
return parseGroup(items)
return ParseGroup(items)
}
// GetGroupExtended gets extended group.
@ -438,7 +472,7 @@ func (c Client) GetGroupExtended(namespace string, groupID int64) (*GroupExtende
return nil, err
}
return parseGroupExtended(items)
return ParseGroupExtended(items)
}
// SetGroupName updates subject name.
@ -490,7 +524,7 @@ func (c Client) GetGroupByName(namespace, groupName string) (*Group, error) {
return nil, err
}
return parseGroup(items)
return ParseGroup(items)
}
// ListGroups gets all groups in specific namespace.
@ -500,7 +534,7 @@ func (c Client) ListGroups(namespace string) ([]*Group, error) {
return nil, err
}
return parseGroups(items)
return ParseGroups(items)
}
// AddSubjectToGroup adds a new subject to group.
@ -529,7 +563,7 @@ func (c Client) RemoveSubjectFromGroupCall(addr util.Uint160, groupID int64) (me
// ListGroupSubjects gets all subjects in specific group.
func (c Client) ListGroupSubjects(namespace string, groupID int64) ([]util.Uint160, error) {
return unwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listGroupSubjectsMethod, namespace, groupID))
return UnwrapArrayOfUint160(commonclient.ReadIteratorItems(c.act, iteratorBatchSize, c.contract, listGroupSubjectsMethod, namespace, groupID))
}
// DeleteGroup deletes group.
@ -612,302 +646,3 @@ func (c Client) ListNonEmptyGroups(namespace string) ([]string, error) {
return res, nil
}
func unwrapArrayOfUint160(items []stackitem.Item, err error) ([]util.Uint160, error) {
if err != nil {
return nil, err
}
return unwrap.ArrayOfUint160(makeValidRes(stackitem.NewArray(items)))
}
func makeValidRes(item stackitem.Item) (*result.Invoke, error) {
return &result.Invoke{
Stack: []stackitem.Item{item},
State: vmstate.Halt.String(),
}, nil
}
func makeResFromAppExec(res *state.AppExecResult) (*result.Invoke, error) {
return &result.Invoke{
Stack: res.Stack,
State: res.VMState.String(),
}, nil
}
func parseSubject(structArr []stackitem.Item) (*Subject, error) {
if len(structArr) < 5 {
return nil, errors.New("invalid response subject struct")
}
var (
err error
subj Subject
)
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
if err != nil {
return nil, err
}
if !structArr[1].Equals(stackitem.Null{}) {
subj.AdditionalKeys, err = unwrap.ArrayOfPublicKeys(makeValidRes(structArr[1]))
if err != nil {
return nil, err
}
}
if !structArr[2].Equals(stackitem.Null{}) {
subj.Namespace, err = unwrap.UTF8String(makeValidRes(structArr[2]))
if err != nil {
return nil, err
}
}
if !structArr[2].Equals(stackitem.Null{}) {
subj.Name, err = unwrap.UTF8String(makeValidRes(structArr[3]))
if err != nil {
return nil, err
}
}
subj.KV, err = parseMap(structArr[4])
if err != nil {
return nil, err
}
return &subj, nil
}
func parseSubjectExtended(structArr []stackitem.Item) (*SubjectExtended, error) {
if len(structArr) < 6 {
return nil, errors.New("invalid response subject extended struct")
}
var (
err error
subj SubjectExtended
)
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
if err != nil {
return nil, err
}
if !structArr[1].Equals(stackitem.Null{}) {
subj.AdditionalKeys, err = unwrap.ArrayOfPublicKeys(makeValidRes(structArr[1]))
if err != nil {
return nil, err
}
}
nsBytes, err := structArr[2].TryBytes()
if err != nil {
return nil, err
}
subj.Namespace = string(nsBytes)
nameBytes, err := structArr[3].TryBytes()
if err != nil {
return nil, err
}
subj.Name = string(nameBytes)
subj.KV, err = parseMap(structArr[4])
if err != nil {
return nil, err
}
if !structArr[5].Equals(stackitem.Null{}) {
groupItems, ok := structArr[5].Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid groups field")
}
subj.Groups, err = parseGroups(groupItems)
if err != nil {
return nil, err
}
}
return &subj, nil
}
func parseMap(item stackitem.Item) (map[string]string, error) {
if item.Equals(stackitem.Null{}) {
return nil, nil
}
metaMap, err := unwrap.Map(makeValidRes(item))
if err != nil {
return nil, err
}
meta, ok := metaMap.Value().([]stackitem.MapElement)
if !ok {
return nil, errors.New("invalid map type")
}
res := make(map[string]string, len(meta))
for _, element := range meta {
key, err := element.Key.TryBytes()
if err != nil {
return nil, err
}
val, err := element.Value.TryBytes()
if err != nil {
return nil, err
}
res[string(key)] = string(val)
}
return res, nil
}
func parseNamespace(structArr []stackitem.Item) (*Namespace, error) {
if len(structArr) < 1 {
return nil, errors.New("invalid response namespace struct")
}
name, err := structArr[0].TryBytes()
if err != nil {
return nil, err
}
return &Namespace{Name: string(name)}, nil
}
func parseNamespaceExtended(structArr []stackitem.Item) (*NamespaceExtended, error) {
if len(structArr) < 3 {
return nil, errors.New("invalid response namespace extended struct")
}
name, err := structArr[0].TryBytes()
if err != nil {
return nil, err
}
groupCount, err := structArr[1].TryInteger()
if err != nil {
return nil, err
}
subjectCount, err := structArr[2].TryInteger()
if err != nil {
return nil, err
}
return &NamespaceExtended{
Name: string(name),
GroupsCount: groupCount.Int64(),
SubjectsCount: subjectCount.Int64(),
}, nil
}
func parseNamespaces(items []stackitem.Item) ([]*Namespace, error) {
var err error
res := make([]*Namespace, len(items))
for i := 0; i < len(items); i++ {
arr, ok := items[i].Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid namespace type")
}
res[i], err = parseNamespace(arr)
if err != nil {
return nil, err
}
}
return res, nil
}
func parseGroup(structArr []stackitem.Item) (*Group, error) {
if len(structArr) < 4 {
return nil, errors.New("invalid response group struct")
}
groupID, err := structArr[0].TryInteger()
if err != nil {
return nil, err
}
name, err := structArr[1].TryBytes()
if err != nil {
return nil, err
}
namespace, err := structArr[2].TryBytes()
if err != nil {
return nil, err
}
kvs, err := parseMap(structArr[3])
if err != nil {
return nil, err
}
return &Group{
ID: groupID.Int64(),
Name: string(name),
Namespace: string(namespace),
KV: kvs,
}, nil
}
func parseGroupExtended(structArr []stackitem.Item) (*GroupExtended, error) {
if len(structArr) < 5 {
return nil, errors.New("invalid response group extended struct")
}
groupID, err := structArr[0].TryInteger()
if err != nil {
return nil, err
}
name, err := structArr[1].TryBytes()
if err != nil {
return nil, err
}
namespace, err := structArr[2].TryBytes()
if err != nil {
return nil, err
}
kvs, err := parseMap(structArr[3])
if err != nil {
return nil, err
}
subjectCount, err := structArr[4].TryInteger()
if err != nil {
return nil, err
}
return &GroupExtended{
ID: groupID.Int64(),
Name: string(name),
Namespace: string(namespace),
KV: kvs,
SubjectsCount: subjectCount.Int64(),
}, nil
}
func parseGroups(items []stackitem.Item) ([]*Group, error) {
var err error
res := make([]*Group, len(items))
for i := 0; i < len(items); i++ {
arr, ok := items[i].Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid group type")
}
res[i], err = parseGroup(arr)
if err != nil {
return nil, err
}
}
return res, nil
}

312
frostfsid/client/utils.go Normal file
View file

@ -0,0 +1,312 @@
package client
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
)
func UnwrapArrayOfUint160(items []stackitem.Item, err error) ([]util.Uint160, error) {
if err != nil {
return nil, err
}
return unwrap.ArrayOfUint160(makeValidRes(stackitem.NewArray(items)))
}
func ParseSubject(structArr []stackitem.Item) (*Subject, error) {
if len(structArr) < 5 {
return nil, errors.New("invalid response subject struct")
}
var (
err error
subj Subject
)
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
if err != nil {
return nil, err
}
if !structArr[1].Equals(stackitem.Null{}) {
subj.AdditionalKeys, err = unwrap.ArrayOfPublicKeys(makeValidRes(structArr[1]))
if err != nil {
return nil, err
}
}
if !structArr[2].Equals(stackitem.Null{}) {
subj.Namespace, err = unwrap.UTF8String(makeValidRes(structArr[2]))
if err != nil {
return nil, err
}
}
if !structArr[2].Equals(stackitem.Null{}) {
subj.Name, err = unwrap.UTF8String(makeValidRes(structArr[3]))
if err != nil {
return nil, err
}
}
subj.KV, err = ParseMap(structArr[4])
if err != nil {
return nil, err
}
return &subj, nil
}
func ParseSubjectExtended(structArr []stackitem.Item) (*SubjectExtended, error) {
if len(structArr) < 6 {
return nil, errors.New("invalid response subject extended struct")
}
var (
err error
subj SubjectExtended
)
subj.PrimaryKey, err = unwrap.PublicKey(makeValidRes(structArr[0]))
if err != nil {
return nil, err
}
if !structArr[1].Equals(stackitem.Null{}) {
subj.AdditionalKeys, err = unwrap.ArrayOfPublicKeys(makeValidRes(structArr[1]))
if err != nil {
return nil, err
}
}
nsBytes, err := structArr[2].TryBytes()
if err != nil {
return nil, err
}
subj.Namespace = string(nsBytes)
nameBytes, err := structArr[3].TryBytes()
if err != nil {
return nil, err
}
subj.Name = string(nameBytes)
subj.KV, err = ParseMap(structArr[4])
if err != nil {
return nil, err
}
if !structArr[5].Equals(stackitem.Null{}) {
groupItems, ok := structArr[5].Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid groups field")
}
subj.Groups, err = ParseGroups(groupItems)
if err != nil {
return nil, err
}
}
return &subj, nil
}
func ParseMap(item stackitem.Item) (map[string]string, error) {
if item.Equals(stackitem.Null{}) {
return nil, nil
}
metaMap, err := unwrap.Map(makeValidRes(item))
if err != nil {
return nil, err
}
meta, ok := metaMap.Value().([]stackitem.MapElement)
if !ok {
return nil, errors.New("invalid map type")
}
res := make(map[string]string, len(meta))
for _, element := range meta {
key, err := element.Key.TryBytes()
if err != nil {
return nil, err
}
val, err := element.Value.TryBytes()
if err != nil {
return nil, err
}
res[string(key)] = string(val)
}
return res, nil
}
func ParseNamespace(structArr []stackitem.Item) (*Namespace, error) {
if len(structArr) < 1 {
return nil, errors.New("invalid response namespace struct")
}
name, err := structArr[0].TryBytes()
if err != nil {
return nil, err
}
return &Namespace{Name: string(name)}, nil
}
func ParseNamespaceExtended(structArr []stackitem.Item) (*NamespaceExtended, error) {
if len(structArr) < 3 {
return nil, errors.New("invalid response namespace extended struct")
}
name, err := structArr[0].TryBytes()
if err != nil {
return nil, err
}
groupCount, err := structArr[1].TryInteger()
if err != nil {
return nil, err
}
subjectCount, err := structArr[2].TryInteger()
if err != nil {
return nil, err
}
return &NamespaceExtended{
Name: string(name),
GroupsCount: groupCount.Int64(),
SubjectsCount: subjectCount.Int64(),
}, nil
}
func ParseNamespaces(items []stackitem.Item) ([]*Namespace, error) {
var err error
res := make([]*Namespace, len(items))
for i := 0; i < len(items); i++ {
arr, ok := items[i].Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid namespace type")
}
res[i], err = ParseNamespace(arr)
if err != nil {
return nil, err
}
}
return res, nil
}
func ParseGroup(structArr []stackitem.Item) (*Group, error) {
if len(structArr) < 4 {
return nil, errors.New("invalid response group struct")
}
groupID, err := structArr[0].TryInteger()
if err != nil {
return nil, err
}
name, err := structArr[1].TryBytes()
if err != nil {
return nil, err
}
namespace, err := structArr[2].TryBytes()
if err != nil {
return nil, err
}
kvs, err := ParseMap(structArr[3])
if err != nil {
return nil, err
}
return &Group{
ID: groupID.Int64(),
Name: string(name),
Namespace: string(namespace),
KV: kvs,
}, nil
}
func ParseGroupExtended(structArr []stackitem.Item) (*GroupExtended, error) {
if len(structArr) < 5 {
return nil, errors.New("invalid response group extended struct")
}
groupID, err := structArr[0].TryInteger()
if err != nil {
return nil, err
}
name, err := structArr[1].TryBytes()
if err != nil {
return nil, err
}
namespace, err := structArr[2].TryBytes()
if err != nil {
return nil, err
}
kvs, err := ParseMap(structArr[3])
if err != nil {
return nil, err
}
subjectCount, err := structArr[4].TryInteger()
if err != nil {
return nil, err
}
return &GroupExtended{
ID: groupID.Int64(),
Name: string(name),
Namespace: string(namespace),
KV: kvs,
SubjectsCount: subjectCount.Int64(),
}, nil
}
func ParseGroups(items []stackitem.Item) ([]*Group, error) {
var err error
res := make([]*Group, len(items))
for i := 0; i < len(items); i++ {
arr, ok := items[i].Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid group type")
}
res[i], err = ParseGroup(arr)
if err != nil {
return nil, err
}
}
return res, nil
}
func makeValidRes(item stackitem.Item) (*result.Invoke, error) {
return &result.Invoke{
Stack: []stackitem.Item{item},
State: vmstate.Halt.String(),
}, nil
}
func makeResFromAppExec(res *state.AppExecResult) (*result.Invoke, error) {
return &result.Invoke{
Stack: res.Stack,
State: res.VMState.String(),
}, nil
}

View file

@ -14,6 +14,13 @@ import (
)
type (
// Subject represents a subject entity.
//
// Fields:
// - Namespace: a string representing the namespace of the subject.
// - Name: a string representing the name of the subject.
// The name must match the following regex pattern: ^[\w+=,.@-]{1,64}$
// The Subject is stored in the storage as a hash(namespace) + hash(name).
Subject struct {
PrimaryKey interop.PublicKey
AdditionalKeys []interop.PublicKey
@ -31,6 +38,15 @@ type (
Groups []Group
}
// Namespace represents a namespace.
//
// Fields:
// - Name: a string representing the name of the namespace.
// The custom name must match the following regex pattern:
// (^$)|(^[a-z0-9]{1,2}$)|(^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$)
// An empty Name is considered the <root> namespace.
//
// The Namespace is stored in the storage as a hash(name).
Namespace struct {
Name string
}
@ -41,6 +57,18 @@ type (
SubjectsCount int
}
// Group represents a group entity.
//
// Fields:
// - ID: an integer representing the unique identifier of the group.
// The ID is generated upon creation and is the sequential number of the group within the namespace.
// It cannot be changed once set.
// - Name: a string representing the name of the group.
// The name must match the following regex pattern: [\w+=,.@-]{1,128}$
// - Namespace: a string representing the namespace of the group.
// A group exists only within one namespace.
//
// The group is stored in the storage as a hash(namespace) + hash(name).
Group struct {
ID int
Name string
@ -90,6 +118,7 @@ func _deploy(data any, isUpdate bool) {
runtime.Log("frostfsid contract initialized")
}
// SetAdmin sets the admin address for the contract.
func SetAdmin(addr interop.Hash160) {
ctx := storage.GetContext()
if !common.HasUpdateAccess() {
@ -99,6 +128,7 @@ func SetAdmin(addr interop.Hash160) {
storage.Put(ctx, adminKey, addr)
}
// ClearAdmin removes the admin address from the contract storage.
func ClearAdmin() {
ctx := storage.GetContext()
if !common.HasUpdateAccess() {
@ -108,6 +138,7 @@ func ClearAdmin() {
storage.Delete(ctx, adminKey)
}
// GetAdmin retrieves the admin address from the contract storage.
func GetAdmin() interop.Hash160 {
ctx := storage.GetReadOnlyContext()
return storage.Get(ctx, adminKey).(interop.Hash160)
@ -129,6 +160,7 @@ func Version() int {
return common.Version
}
// CreateSubject creates a new subject in the specified namespace with the provided public key.
func CreateSubject(ns string, key interop.PublicKey) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -169,6 +201,7 @@ func CreateSubject(ns string, key interop.PublicKey) {
runtime.Notify("CreateSubject", interop.Hash160(addr))
}
// AddSubjectKey adds an additional public key to a subject with the specified address.
func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -200,6 +233,7 @@ func AddSubjectKey(addr interop.Hash160, key interop.PublicKey) {
runtime.Notify("AddSubjectKey", addr, key)
}
// RemoveSubjectKey removes an additional public key from the subject with the specified address.
func RemoveSubjectKey(addr interop.Hash160, key interop.PublicKey) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -238,6 +272,7 @@ func RemoveSubjectKey(addr interop.Hash160, key interop.PublicKey) {
runtime.Notify("RemoveSubjectKey", addr, key)
}
// SetSubjectName sets a new name for the subject with the specified address.
func SetSubjectName(addr interop.Hash160, name string) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -262,6 +297,7 @@ func SetSubjectName(addr interop.Hash160, name string) {
runtime.Notify("SetSubjectName", addr, name)
}
// SetSubjectKV sets a key-value pair for the subject with the specified address.
func SetSubjectKV(addr interop.Hash160, key, val string) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -286,6 +322,7 @@ func SetSubjectKV(addr interop.Hash160, key, val string) {
runtime.Notify("SetSubjectKV", addr, key, val)
}
// DeleteSubjectKV deletes a key-value pair from the subject with the specified address.
func DeleteSubjectKV(addr interop.Hash160, key string) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -307,6 +344,7 @@ func DeleteSubjectKV(addr interop.Hash160, key string) {
runtime.Notify("DeleteSubjectKV", addr, key)
}
// DeleteSubject deletes the subject with the specified address.
func DeleteSubject(addr interop.Hash160) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -333,6 +371,7 @@ func DeleteSubject(addr interop.Hash160) {
runtime.Notify("DeleteSubject", addr)
}
// GetSubject retrieves the subject with the specified address.
func GetSubject(addr interop.Hash160) Subject {
if len(addr) != interop.Hash160Len {
panic("incorrect address length")
@ -348,6 +387,7 @@ func GetSubject(addr interop.Hash160) Subject {
return std.Deserialize(data).(Subject)
}
// GetSubjectExtended retrieves the extended information of the subject with the specified address.
func GetSubjectExtended(addr interop.Hash160) SubjectExtended {
subj := GetSubject(addr)
ctx := storage.GetReadOnlyContext()
@ -379,6 +419,7 @@ func GetSubjectExtended(addr interop.Hash160) SubjectExtended {
return subjExt
}
// GetSubjectByKey retrieves the subject associated with the provided public key.
func GetSubjectByKey(key interop.PublicKey) Subject {
if len(key) != interop.PublicKeyCompressedLen {
panic("incorrect key length")
@ -407,12 +448,14 @@ func GetSubjectByKey(key interop.PublicKey) Subject {
panic("subject not found")
}
// GetSubjectByName retrieves the subject with the specified name within the given namespace.
func GetSubjectByName(ns, name string) Subject {
key := GetSubjectKeyByName(ns, name)
addr := contract.CreateStandardAccount(key)
return GetSubject(addr)
}
// GetSubjectKeyByName retrieves the public key of the subject with the specified namespace and name.
func GetSubjectKeyByName(ns, name string) interop.PublicKey {
if name == "" {
panic("invalid or name")
@ -434,6 +477,7 @@ func ListSubjects() iterator.Iterator {
return storage.Find(ctx, []byte{subjectKeysPrefix}, storage.KeysOnly|storage.RemovePrefix)
}
// CreateNamespace creates a new namespace with the specified name.
func CreateNamespace(ns string) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -452,6 +496,7 @@ func CreateNamespace(ns string) {
runtime.Notify("CreateNamespace", ns)
}
// GetNamespace retrieves the namespace with the specified name.
func GetNamespace(ns string) Namespace {
ctx := storage.GetReadOnlyContext()
nsKey := namespaceKey(ns)
@ -463,6 +508,7 @@ func GetNamespace(ns string) Namespace {
return std.Deserialize(data).(Namespace)
}
// GetNamespaceExtended retrieves extended information about the namespace.
func GetNamespaceExtended(ns string) NamespaceExtended {
ctx := storage.GetReadOnlyContext()
nsKey := namespaceKey(ns)
@ -499,6 +545,7 @@ func ListNamespaceSubjects(ns string) iterator.Iterator {
return storage.Find(ctx, namespaceSubjectPrefix(ns), storage.KeysOnly|storage.RemovePrefix)
}
// CreateGroup creates a new group within the specified namespace.
func CreateGroup(ns, group string) int {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -533,6 +580,7 @@ func CreateGroup(ns, group string) int {
return groupCountID
}
// GetGroup retrieves the group with the specified ID within the given namespace.
func GetGroup(ns string, groupID int) Group {
ctx := storage.GetReadOnlyContext()
gKey := groupKey(ns, groupID)
@ -544,6 +592,7 @@ func GetGroup(ns string, groupID int) Group {
return std.Deserialize(data).(Group)
}
// GetGroupExtended retrieves extended information about the group, including the count of subjects in the group.
func GetGroupExtended(ns string, groupID int) GroupExtended {
ctx := storage.GetReadOnlyContext()
gKey := groupKey(ns, groupID)
@ -569,6 +618,7 @@ func GetGroupExtended(ns string, groupID int) GroupExtended {
return grExtended
}
// GetGroupIDByName retrieves the ID of the group with the specified name within the given namespace.
func GetGroupIDByName(ns, name string) int {
if name == "" {
panic("invalid name")
@ -585,6 +635,7 @@ func GetGroupIDByName(ns, name string) int {
return std.Deserialize(groupIDRaw).(int)
}
// GetGroupByName retrieves the group with the specified name within the given namespace.
func GetGroupByName(ns, name string) Group {
groupID := GetGroupIDByName(ns, name)
gKey := groupKey(ns, groupID)
@ -598,6 +649,7 @@ func GetGroupByName(ns, name string) Group {
return std.Deserialize(data).(Group)
}
// SetGroupName sets a new name for the group with the specified ID within the given namespace.
func SetGroupName(ns string, groupID int, name string) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -618,6 +670,7 @@ func SetGroupName(ns string, groupID int, name string) {
runtime.Notify("SetGroupName", ns, groupID, name)
}
// SetGroupKV sets a key-value pair for the group with the specified ID within the given namespace.
func SetGroupKV(ns string, groupID int, key, val string) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -638,6 +691,7 @@ func SetGroupKV(ns string, groupID int, key, val string) {
runtime.Notify("SetGroupKV", ns, groupID, key, val)
}
// DeleteGroupKV deletes a key-value pair from the group with the specified ID within the given namespace.
func DeleteGroupKV(ns string, groupID int, key string) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -660,6 +714,7 @@ func ListGroups(ns string) iterator.Iterator {
return storage.Find(ctx, groupPrefix(ns), storage.ValuesOnly|storage.DeserializeValues)
}
// AddSubjectToGroup adds a subject to a group with the specified ID.
func AddSubjectToGroup(addr interop.Hash160, groupID int) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -692,6 +747,7 @@ func AddSubjectToGroup(addr interop.Hash160, groupID int) {
runtime.Notify("AddSubjectToGroup", addr, subject.Namespace, groupID)
}
// RemoveSubjectFromGroup removes a subject from a group with the specified ID.
func RemoveSubjectFromGroup(addr interop.Hash160, groupID int) {
ctx := storage.GetContext()
checkContractOwner(ctx)
@ -730,6 +786,7 @@ func ListGroupSubjects(ns string, groupID int) iterator.Iterator {
return storage.Find(ctx, groupSubjectPrefix(ns, groupID), storage.KeysOnly|storage.RemovePrefix)
}
// DeleteGroup deletes the group with the specified ID within the given namespace.
func DeleteGroup(ns string, groupID int) {
ctx := storage.GetContext()
checkContractOwner(ctx)

67
go.mod
View file

@ -3,12 +3,12 @@ module git.frostfs.info/TrueCloudLab/frostfs-contract
go 1.20
require (
github.com/google/uuid v1.3.1
github.com/google/uuid v1.6.0
github.com/mr-tron/base58 v1.2.0
github.com/nspcc-dev/neo-go v0.105.0
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0
github.com/stretchr/testify v1.8.4
go.uber.org/zap v1.26.0
github.com/nspcc-dev/neo-go v0.106.3
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240727093519-1a48f1ce43ec
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
)
require (
@ -17,46 +17,39 @@ require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/golang-lru v0.6.0 // indirect
github.com/holiman/uint256 v1.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c // indirect
github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c // indirect
github.com/nspcc-dev/neofs-crypto v0.4.0 // indirect
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
github.com/nspcc-dev/dbft v0.2.0 // indirect
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 // indirect
github.com/nspcc-dev/rfc6979 v0.2.1 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.13.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/prometheus/client_golang v1.19.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect
github.com/twmb/murmur3 v1.1.5 // indirect
github.com/urfave/cli v1.22.5 // indirect
go.etcd.io/bbolt v1.3.8 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
github.com/twmb/murmur3 v1.1.8 // indirect
github.com/urfave/cli/v2 v2.27.2 // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
go.etcd.io/bbolt v1.3.9 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)

579
go.sum
View file

@ -1,214 +1,72 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h1:npHgfD4Tl2WJS3AJaMUi5ynGDPUBfkg3U3fCzDyXZ+4=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
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=
github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
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/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
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=
github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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/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=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4=
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
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=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
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=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c h1:uyK5aLbAhrnZtnvobJLN24gGUrlxIJAAFqiWl+liZuo=
github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c/go.mod h1:kjBC9F8L25GR+kIHy/1KgG/KfcoGnVwIiyovgq1uszk=
github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c h1:OOQeE613BH93ICPq3eke5N78gWNeMjcBWkmD2NKyXVg=
github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U=
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
github.com/nspcc-dev/neo-go v0.105.0 h1:vtNZYFEFySK8zRDhLzQYha849VzWrcKezlnq/oNQg/w=
github.com/nspcc-dev/neo-go v0.105.0/go.mod h1:6pchIHg5okeZO955RxpTh5q0sUI0vtpgPM6Q+no1rlI=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0 h1:N+dMIBmteXjJpkH6UZ7HmNftuFxkqszfGLbhsEctnv0=
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag=
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs=
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg=
github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
github.com/nspcc-dev/tzhash v1.7.0 h1:/+aL33NC7y5OIGnY2kYgjZt8mg7LVGFMdj/KAJLndnk=
github.com/nspcc-dev/dbft v0.2.0 h1:sDwsQES600OSIMncV176t2SX5OvB14lzeOAyKFOkbMI=
github.com/nspcc-dev/dbft v0.2.0/go.mod h1:oFE6paSC/yfFh9mcNU6MheMGOYXK9+sPiRk3YMoz49o=
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/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-sdk-go v1.0.0-rc.12 h1:mdxtlSU2I4oVZ/7AXTLKyz8uUPbDWikZw4DM8gvrddA=
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/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=
@ -220,392 +78,95 @@ github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
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/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=
github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/twmb/murmur3 v1.1.5 h1:i9OLS9fkuLzBXjt6dptlAEyk58fJsSTXbRg3SgVyqgk=
github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo=
github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1:RmMWU37GKR2s6pgrIEB4ixgpVCt/cf7dnJv3fuH1J1c=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk=
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/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=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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/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=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
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.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 h1:DC7wcm+i+P1rN3Ff07vL+OndGg5OhNddHyTA+ocPqYE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
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=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
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.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=

View file

@ -4,6 +4,26 @@ safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "own
"tokens", "properties", "roots", "getPrice", "isAvailable", "getRecords",
"resolve", "version"]
events:
- name: RegisterDomain
parameters:
- name: name
type: String
- name: AddRecord
parameters:
- name: name
type: String
- name: type
type: Integer
- name: DeleteRecords
parameters:
- name: name
type: String
- name: type
type: Integer
- name: DeleteDomain
parameters:
- name: name
type: String
- name: Transfer
parameters:
- name: from

View file

@ -11,6 +11,7 @@
| 0x20 | int | set of roots |
| 0x21 + tokenKey | ByteArray | serialized NameState struct |
| 0x22 + tokenKey + Hash160(tokenName) | Hash160 | container contract hash |
| 0x23 + tokenKey + Hash160(tokenName) | string | global domain flag |
*/

View file

@ -1,32 +0,0 @@
name: "NameService"
supportedstandards: ["NEP-11"]
safemethods:
- "balanceOf"
- "decimals"
- "getAllRecords"
- "getPrice"
- "getRecord"
- "isAvailable"
- "ownerOf"
- "properties"
- "symbol"
- "totalSupply"
- "tokensOf"
- "tokens"
- "resolve"
- "roots"
events:
- name: Transfer
parameters:
- name: from
type: Hash160
- name: to
type: Hash160
- name: amount
type: Integer
- name: tokenId
type: ByteArray
permissions:
- hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
methods: ["update"]
- methods: ["onNEP11Payment"]

View file

@ -41,6 +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 byte = 0x23
)
// Values constraints.
@ -65,6 +68,14 @@ const (
defaultRegisterPrice = 10_0000_0000
// millisecondsInYear is amount of milliseconds per year.
millisecondsInYear = int64(365 * 24 * 3600 * 1000)
// errInvalidDomainName is an error message for invalid domain name format.
errInvalidDomainName = "invalid domain name format"
)
const (
// Cnametgt is a special TXT record ensuring all created subdomains point to the global domain - the value of this variable.
//It is guaranteed that two domains cannot point to the same global domain.
Cnametgt = "cnametgt"
)
// RecordState is a type that registered entities are saved to.
@ -220,9 +231,6 @@ func GetPrice() int {
// IsAvailable checks whether the provided domain name is available.
func IsAvailable(name string) bool {
fragments := splitAndCheck(name)
if fragments == nil {
panic("invalid domain name format")
}
ctx := storage.GetReadOnlyContext()
l := len(fragments)
if storage.Get(ctx, append([]byte{prefixRoot}, []byte(fragments[l-1])...)) == nil {
@ -231,15 +239,81 @@ func IsAvailable(name string) bool {
}
return true
}
if parentExpired(ctx, fragments) {
panic("parent does not exist or is expired")
}
checkParentExists(ctx, fragments)
checkAvailableGlobalDomain(ctx, name)
return storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...)) == nil
}
// parentExpired returns true if any domain from fragments doesn't exist or is expired.
// checkAvailableGlobalDomain - triggers a panic if the global domain name is occupied.
func checkAvailableGlobalDomain(ctx storage.Context, domain string) {
globalDomain := getGlobalDomain(ctx, domain)
if globalDomain == "" {
return
}
nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(globalDomain))...))
if nsBytes != nil {
panic("global domain is already taken: " + globalDomain + ". Domain: " + domain)
}
}
// getGlobalDomain returns the global domain.
func getGlobalDomain(ctx storage.Context, domain string) string {
index := std.MemorySearch([]byte(domain), []byte("."))
if index == -1 {
return ""
}
name := domain[index+1:]
if name == "" {
return ""
}
return extractCnametgt(ctx, name, domain)
}
// extractCnametgt returns the value of the Cnametgt TXT record.
func extractCnametgt(ctx storage.Context, name, domain string) string {
fragments := splitAndCheck(domain)
tokenID := []byte(tokenIDFromName(name))
records := getRecordsByType(ctx, tokenID, name, TXT)
if records == nil {
return ""
}
globalDomain := ""
for _, name := range records {
fragments := std.StringSplit(name, "=")
if len(fragments) != 2 {
continue
}
if fragments[0] == Cnametgt {
globalDomain = fragments[1]
break
}
}
if globalDomain == "" {
return ""
}
return fragments[0] + "." + globalDomain
}
// checkParentExists panics if any domain from fragments doesn't exist or is expired.
func checkParentExists(ctx storage.Context, fragments []string) {
if dom := parentExpired(ctx, fragments); dom != "" {
panic("domain does not exist or is expired: " + dom)
}
}
// parentExpired returns domain from fragments that doesn't exist or is expired.
// first denotes the deepest subdomain to check.
func parentExpired(ctx storage.Context, fragments []string) bool {
func parentExpired(ctx storage.Context, fragments []string) string {
now := int64(runtime.GetTime())
last := len(fragments) - 1
name := fragments[last]
@ -249,26 +323,28 @@ func parentExpired(ctx storage.Context, fragments []string) bool {
}
nsBytes := storage.Get(ctx, append([]byte{prefixName}, getTokenKey([]byte(name))...))
if nsBytes == nil {
return true
return name
}
ns := std.Deserialize(nsBytes.([]byte)).(NameState)
if now >= ns.Expiration {
return true
return name
}
}
return false
return ""
}
// Register registers a new domain with the specified owner and name if it's available.
func Register(name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool {
fragments := splitAndCheck(name)
if fragments == nil {
panic("invalid domain name format")
}
ctx := storage.GetContext()
return register(ctx, name, owner, email, refresh, retry, expire, ttl)
}
// Register registers a new domain with the specified owner and name if it's available.
func register(ctx storage.Context, name string, owner interop.Hash160, email string, refresh, retry, expire, ttl int) bool {
fragments := splitAndCheck(name)
l := len(fragments)
tldKey := append([]byte{prefixRoot}, []byte(fragments[l-1])...)
ctx := storage.GetContext()
tldBytes := storage.Get(ctx, tldKey)
if l == 1 {
checkCommittee()
@ -280,9 +356,8 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
if tldBytes == nil {
panic("TLD not found")
}
if parentExpired(ctx, fragments) {
panic("one of the parent domains is not registered")
}
checkParentExists(ctx, fragments)
parentKey := getTokenKey([]byte(name[len(fragments[0])+1:]))
nsBytes := storage.Get(ctx, append([]byte{prefixName}, parentKey...))
ns := std.Deserialize(nsBytes.([]byte)).(NameState)
@ -326,18 +401,19 @@ func Register(name string, owner interop.Hash160, email string, refresh, retry,
// NNS expiration is in milliseconds
Expiration: int64(runtime.GetTime() + expire*1000),
}
checkAvailableGlobalDomain(ctx, name)
putNameStateWithKey(ctx, tokenKey, ns)
putSoaRecord(ctx, name, email, refresh, retry, expire, ttl)
updateBalance(ctx, []byte(name), owner, +1)
postTransfer(oldOwner, owner, []byte(name), nil)
runtime.Notify("RegisterDomain", name)
return true
}
// Renew increases domain expiration date.
func Renew(name string) int64 {
if len(name) > maxDomainNameLength {
panic("invalid domain name format")
}
checkDomainNameLength(name)
runtime.BurnGas(GetPrice())
ctx := storage.GetContext()
ns := getNameState(ctx, []byte(name))
@ -349,9 +425,7 @@ func Renew(name string) int64 {
// UpdateSOA updates soa record.
func UpdateSOA(name, email string, refresh, retry, expire, ttl int) {
if len(name) > maxDomainNameLength {
panic("invalid domain name format")
}
checkDomainNameLength(name)
ctx := storage.GetContext()
ns := getNameState(ctx, []byte(name))
ns.checkAdmin()
@ -360,9 +434,7 @@ func UpdateSOA(name, email string, refresh, retry, expire, ttl int) {
// SetAdmin updates domain admin.
func SetAdmin(name string, admin interop.Hash160) {
if len(name) > maxDomainNameLength {
panic("invalid domain name format")
}
checkDomainNameLength(name)
if admin != nil && !runtime.CheckWitness(admin) {
panic("not witnessed by admin")
}
@ -425,13 +497,26 @@ func GetRecords(name string, typ RecordType) []string {
// DeleteRecords removes domain records with the specified type.
func DeleteRecords(name string, typ RecordType) {
ctx := storage.GetContext()
deleteRecords(ctx, name, typ)
}
// DeleteRecords removes domain records with the specified type.
func deleteRecords(ctx storage.Context, name string, typ RecordType) {
if typ == SOA {
panic("you cannot delete soa record")
}
tokenID := []byte(tokenIDFromName(name))
ctx := storage.GetContext()
ns := getNameState(ctx, tokenID)
ns.checkAdmin()
globalDomainStorage := append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...)
globalDomainRaw := storage.Get(ctx, globalDomainStorage)
globalDomain := globalDomainRaw.(string)
if globalDomainRaw != nil && globalDomain != "" {
deleteDomain(ctx, globalDomain)
}
recordsKey := getRecordsKeyByType(tokenID, name, typ)
records := storage.Find(ctx, recordsKey, storage.KeysOnly)
for iterator.Next(records) {
@ -439,6 +524,34 @@ func DeleteRecords(name string, typ RecordType) {
storage.Delete(ctx, r)
}
updateSoaSerial(ctx, tokenID)
runtime.Notify("DeleteRecords", name, typ)
}
// DeleteDomain deletes the domain with the given name.
func DeleteDomain(name string) {
ctx := storage.GetContext()
deleteDomain(ctx, name)
}
func deleteDomain(ctx storage.Context, name string) {
nameKey := append([]byte{prefixName}, getTokenKey([]byte(name))...)
tldBytes := storage.Get(ctx, nameKey)
if tldBytes == nil {
return
}
globalDomainRaw := storage.Get(ctx, append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...))
globalDomain := globalDomainRaw.(string)
if globalDomainRaw != nil && globalDomain != "" {
deleteDomain(ctx, globalDomain)
}
deleteRecords(ctx, name, CNAME)
deleteRecords(ctx, name, TXT)
deleteRecords(ctx, name, A)
deleteRecords(ctx, name, AAAA)
storage.Delete(ctx, nameKey)
runtime.Notify("DeleteDomain", name)
}
// Resolve resolves given name (not more then three redirects are allowed).
@ -511,9 +624,7 @@ func getNameState(ctx storage.Context, tokenID []byte) NameState {
tokenKey := getTokenKey(tokenID)
ns := getNameStateWithKey(ctx, tokenKey)
fragments := std.StringSplit(string(tokenID), ".")
if parentExpired(ctx, fragments) {
panic("parent domain has expired")
}
checkParentExists(ctx, fragments)
return ns
}
@ -583,6 +694,33 @@ func addRecord(ctx storage.Context, tokenId []byte, name string, typ RecordType,
}
}
globalDomainKey := append([]byte{prefixGlobalDomain}, getTokenKey([]byte(name))...)
globalDomainStorage := storage.Get(ctx, globalDomainKey)
globalDomain := getGlobalDomain(ctx, name)
if globalDomainStorage == nil && typ == TXT {
if globalDomain != "" {
checkAvailableGlobalDomain(ctx, name)
nsOriginal := getNameState(ctx, []byte(tokenIDFromName(name)))
ns := NameState{
Name: globalDomain,
Owner: nsOriginal.Owner,
Expiration: nsOriginal.Expiration,
Admin: nsOriginal.Admin,
}
putNameStateWithKey(ctx, getTokenKey([]byte(globalDomain)), ns)
storage.Put(ctx, globalDomainKey, globalDomain)
var oldOwner interop.Hash160
updateBalance(ctx, []byte(name), nsOriginal.Owner, +1)
postTransfer(oldOwner, nsOriginal.Owner, []byte(name), nil)
putCnameRecord(ctx, globalDomain, name)
} else {
storage.Put(ctx, globalDomainKey, "")
}
}
if typ == CNAME && id != 0 {
panic("you shouldn't have more than one CNAME record")
}
@ -601,6 +739,7 @@ func storeRecord(ctx storage.Context, recordKey []byte, name string, typ RecordT
}
recBytes := std.Serialize(rs)
storage.Put(ctx, recordKey, recBytes)
runtime.Notify("AddRecord", name, typ)
}
// putSoaRecord stores soa domain record.
@ -621,6 +760,24 @@ func putSoaRecord(ctx storage.Context, name, email string, refresh, retry, expir
}
recBytes := std.Serialize(rs)
storage.Put(ctx, recordKey, recBytes)
runtime.Notify("AddRecord", name, SOA)
}
// putCnameRecord stores CNAME domain record.
func putCnameRecord(ctx storage.Context, name, data string) {
var id byte
tokenId := []byte(tokenIDFromName(name))
recordKey := getIdRecordKey(tokenId, name, CNAME, id)
rs := RecordState{
Name: name,
Type: CNAME,
ID: id,
Data: data,
}
recBytes := std.Serialize(rs)
storage.Put(ctx, recordKey, recBytes)
runtime.Notify("AddRecord", name, CNAME)
}
// updateSoaSerial stores soa domain record.
@ -630,7 +787,7 @@ func updateSoaSerial(ctx storage.Context, tokenId []byte) {
recBytes := storage.Get(ctx, recordKey)
if recBytes == nil {
panic("not found soa record")
return
}
rec := std.Deserialize(recBytes.([]byte)).(RecordState)
@ -720,20 +877,28 @@ func isAlNum(c uint8) bool {
// splitAndCheck splits domain name into parts and validates it.
func splitAndCheck(name string) []string {
l := len(name)
if l < minDomainNameLength || maxDomainNameLength < l {
return nil
}
checkDomainNameLength(name)
fragments := std.StringSplit(name, ".")
l = len(fragments)
l := len(fragments)
for i := 0; i < l; i++ {
if !checkFragment(fragments[i], i == l-1) {
return nil
panic(errInvalidDomainName + " '" + name + "': invalid fragment '" + fragments[i] + "'")
}
}
return fragments
}
// checkDomainNameLength panics if domain name length is out of boundaries.
func checkDomainNameLength(name string) {
l := len(name)
if l > maxDomainNameLength {
panic(errInvalidDomainName + " '" + name + "': domain name too long: got = " + std.Itoa(l, 10) + ", max = " + std.Itoa(maxDomainNameLength, 10))
}
if l < minDomainNameLength {
panic(errInvalidDomainName + " '" + name + "': domain name too short: got = " + std.Itoa(l, 10) + ", min = " + std.Itoa(minDomainNameLength, 10))
}
}
// checkIPv4 checks record on IPv4 compliance.
func checkIPv4(data string) bool {
l := len(data)
@ -847,9 +1012,6 @@ func checkIPv6(data string) bool {
// tokenIDFromName returns token ID (domain.root) from the provided name.
func tokenIDFromName(name string) string {
fragments := splitAndCheck(name)
if fragments == nil {
panic("invalid domain name format")
}
ctx := storage.GetReadOnlyContext()
sum := 0

View file

@ -1,8 +1,12 @@
name: "APE"
permissions:
- methods: ["update"]
safemethods:
- "getAdmin"
- "listChains"
- "getChain"
- "listChainsByPrefix"
- "listTargets"
- "listChainNames"
- "iteratorChainsByPrefix"
- "version"

View file

@ -7,7 +7,7 @@
| 'c' + uint16(len(container)) + container | []byte | Namespace chain |
| 'n' + uint16(len(namespace)) + namespace | []byte | Container chain |
| 'm' + entity name (namespace/container) | []byte | Mapped name to an encoded number |
| 'counter' | uint64 | Integer counter used for mapping |
| 'Counter' | uint64 | Integer counter used for mapping |
*/

View file

@ -4,6 +4,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
@ -15,6 +16,8 @@ type Kind byte
const (
Namespace = 'n'
Container = 'c'
User = 'u'
Group = 'g'
IAM = 'i'
)
@ -24,7 +27,7 @@ const (
const (
mappingKeyPrefix = 'm'
counterKey = "counter"
counterKey = "Counter"
)
const (
@ -36,6 +39,8 @@ const (
// _deploy function sets up initial list of inner ring public keys.
func _deploy(data any, isUpdate bool) {
if isUpdate {
args := data.([]any)
common.CheckVersion(args[len(args)-1].(int))
return
}
@ -69,6 +74,17 @@ func Version() int {
return common.Version
}
// Update method updates contract source code and manifest. It can be invoked
// by committee only.
func Update(script []byte, manifest []byte, data any) {
if !common.HasUpdateAccess() {
panic("only committee can update contract")
}
management.UpdateWithData(script, manifest, common.AppendVersion(data))
runtime.Log("policy contract updated")
}
func SetAdmin(addr interop.Hash160) {
common.CheckAlphabetWitness()
@ -90,14 +106,18 @@ func storageKey(prefix Kind, counter int, name []byte) []byte {
return append(key, name...)
}
func mapKey(kind Kind, name []byte) []byte {
return append([]byte{mappingKeyPrefix, byte(kind)}, name...)
}
// mapToNumeric maps a name to a number. That allows to keep more space in
// a storage key shortening long names. Short entity
// names are also mapped to prevent collisions in the map.
func mapToNumeric(ctx storage.Context, name []byte) (mapped int, mappingExists bool) {
mKey := append([]byte{mappingKeyPrefix}, name...)
func mapToNumeric(ctx storage.Context, kind Kind, name []byte) (mapped int, mappingExists bool) {
mKey := mapKey(kind, name)
numericID := storage.Get(ctx, mKey)
if numericID == nil {
return
return 0, false
}
mapped = numericID.(int)
mappingExists = true
@ -109,8 +129,8 @@ func mapToNumeric(ctx storage.Context, name []byte) (mapped int, mappingExists b
// names are also mapped to prevent collisions in the map.
// If a mapping cannot be found, then the method creates and returns it.
// mapToNumericCreateIfNotExists is NOT applicable for a read-only context.
func mapToNumericCreateIfNotExists(ctx storage.Context, name []byte) int {
mKey := append([]byte{mappingKeyPrefix}, name...)
func mapToNumericCreateIfNotExists(ctx storage.Context, kind Kind, name []byte) int {
mKey := mapKey(kind, name)
numericID := storage.Get(ctx, mKey)
if numericID == nil {
counter := storage.Get(ctx, counterKey).(int)
@ -126,7 +146,7 @@ func AddChain(entity Kind, entityName string, name []byte, chain []byte) {
ctx := storage.GetContext()
checkAuthorization(ctx)
entityNameBytes := mapToNumericCreateIfNotExists(ctx, []byte(entityName))
entityNameBytes := mapToNumericCreateIfNotExists(ctx, entity, []byte(entityName))
key := storageKey(entity, entityNameBytes, name)
storage.Put(ctx, key, chain)
}
@ -134,7 +154,7 @@ func AddChain(entity Kind, entityName string, name []byte, chain []byte) {
func GetChain(entity Kind, entityName string, name []byte) []byte {
ctx := storage.GetReadOnlyContext()
entityNameBytes, exists := mapToNumeric(ctx, []byte(entityName))
entityNameBytes, exists := mapToNumeric(ctx, entity, []byte(entityName))
if !exists {
panic("not found")
}
@ -152,29 +172,43 @@ func RemoveChain(entity Kind, entityName string, name []byte) {
ctx := storage.GetContext()
checkAuthorization(ctx)
entityNameBytes, exists := mapToNumeric(ctx, []byte(entityName))
entityNameNum, exists := mapToNumeric(ctx, entity, []byte(entityName))
if !exists {
return
}
key := storageKey(entity, entityNameBytes, name)
key := storageKey(entity, entityNameNum, name)
storage.Delete(ctx, key)
// If no chains are left for the target, then remove the mapping.
prefix := append([]byte{byte(entity)}, common.ToFixedWidth64(entityNameNum)...)
it := storage.Find(ctx, prefix, storage.KeysOnly)
if !iterator.Next(it) {
storage.Delete(ctx, mapKey(entity, []byte(entityName)))
}
}
func RemoveChainsByPrefix(entity Kind, entityName string, name []byte) {
ctx := storage.GetContext()
checkAuthorization(ctx)
entityNameBytes, exists := mapToNumeric(ctx, []byte(entityName))
entityNameNum, exists := mapToNumeric(ctx, entity, []byte(entityName))
if !exists {
return
}
key := storageKey(entity, entityNameBytes, name)
key := storageKey(entity, entityNameNum, name)
it := storage.Find(ctx, key, storage.KeysOnly)
for iterator.Next(it) {
storage.Delete(ctx, iterator.Value(it).([]byte))
}
// If no chains are left for the target, then remove the mapping.
prefix := append([]byte{byte(entity)}, common.ToFixedWidth64(entityNameNum)...)
it = storage.Find(ctx, prefix, storage.KeysOnly)
if !iterator.Next(it) {
storage.Delete(ctx, mapKey(entity, []byte(entityName)))
}
}
// ListChains lists all chains for the namespace by prefix.
@ -195,7 +229,7 @@ func ListChainsByPrefix(entity Kind, entityName string, prefix []byte) [][]byte
result := [][]byte{}
entityNameBytes, exists := mapToNumeric(ctx, []byte(entityName))
entityNameBytes, exists := mapToNumeric(ctx, entity, []byte(entityName))
if !exists {
return result
}
@ -211,7 +245,22 @@ func ListChainsByPrefix(entity Kind, entityName string, prefix []byte) [][]byte
func IteratorChainsByPrefix(entity Kind, entityName string, prefix []byte) iterator.Iterator {
ctx := storage.GetReadOnlyContext()
id, _ := mapToNumeric(ctx, []byte(entityName))
id, _ := mapToNumeric(ctx, entity, []byte(entityName))
keyPrefix := storageKey(entity, id, prefix)
return storage.Find(ctx, keyPrefix, storage.ValuesOnly)
}
// ListTargets iterates over targets for which rules are defined.
func ListTargets(entity Kind) iterator.Iterator {
ctx := storage.GetReadOnlyContext()
mKey := mapKey(entity, []byte{})
return storage.Find(ctx, mKey, storage.KeysOnly|storage.RemovePrefix)
}
// ListChainNames iterates over chain names for specific target.
func ListChainNames(entity Kind, entityName string) iterator.Iterator {
ctx := storage.GetReadOnlyContext()
id, _ := mapToNumeric(ctx, entity, []byte(entityName))
keyPrefix := storageKey(entity, id, []byte{})
return storage.Find(ctx, keyPrefix, storage.KeysOnly|storage.RemovePrefix)
}

View file

@ -3,19 +3,11 @@ package processing
import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"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/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
const (
frostfsContractKey = "frostfsScriptHash"
multiaddrMethod = "alphabetAddress"
)
// OnNEP17Payment is a callback for NEP-17 compatible native GAS contract.
@ -33,18 +25,6 @@ func _deploy(data any, isUpdate bool) {
return
}
args := data.(struct {
addrFrostFS interop.Hash160
})
ctx := storage.GetContext()
if len(args.addrFrostFS) != interop.Hash160Len {
panic("incorrect length of contract script hash")
}
storage.Put(ctx, frostfsContractKey, args.addrFrostFS)
runtime.Log("processing contract initialized")
}
@ -66,11 +46,7 @@ func Update(script []byte, manifest []byte, data any) {
// Verify method returns true if transaction contains valid multisignature of
// Alphabet nodes of the Inner Ring.
func Verify() bool {
ctx := storage.GetContext()
frostfsContractAddr := storage.Get(ctx, frostfsContractKey).(interop.Hash160)
multiaddr := contract.Call(frostfsContractAddr, multiaddrMethod, contract.ReadOnly).(interop.Hash160)
return runtime.CheckWitness(multiaddr)
return runtime.CheckWitness(common.AlphabetAddress())
}
// Version returns the version of the contract.

View file

@ -103,11 +103,6 @@ func New(actor Actor, hash util.Uint160) *Contract {
return &Contract{ContractReader{actor, hash}, actor, hash}
}
// AlphabetAddress invokes `alphabetAddress` method of contract.
func (c *ContractReader) AlphabetAddress() (util.Uint160, error) {
return unwrap.Uint160(c.invoker.Call(c.hash, "alphabetAddress"))
}
// Config invokes `config` method of contract.
func (c *ContractReader) Config(key []byte) (any, error) {
return func(item stackitem.Item, err error) (any, error) {

View file

@ -4,6 +4,8 @@
package nameservice
import (
"errors"
"fmt"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
@ -13,8 +15,31 @@ import (
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
"unicode/utf8"
)
// RegisterDomainEvent represents "RegisterDomain" event emitted by the contract.
type RegisterDomainEvent struct {
Name string
}
// AddRecordEvent represents "AddRecord" event emitted by the contract.
type AddRecordEvent struct {
Name string
Type *big.Int
}
// DeleteRecordsEvent represents "DeleteRecords" event emitted by the contract.
type DeleteRecordsEvent struct {
Name string
Type *big.Int
}
// DeleteDomainEvent represents "DeleteDomain" event emitted by the contract.
type DeleteDomainEvent struct {
Name string
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
@ -121,6 +146,28 @@ func (c *Contract) AddRecordUnsigned(name string, typ *big.Int, data string) (*t
return c.actor.MakeUnsignedCall(c.hash, "addRecord", nil, name, typ, data)
}
// DeleteDomain creates a transaction invoking `deleteDomain` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) DeleteDomain(name string) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "deleteDomain", name)
}
// DeleteDomainTransaction creates a transaction invoking `deleteDomain` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DeleteDomainTransaction(name string) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "deleteDomain", name)
}
// DeleteDomainUnsigned creates a transaction invoking `deleteDomain` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DeleteDomainUnsigned(name string) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "deleteDomain", nil, name)
}
// DeleteRecords creates a transaction invoking `deleteRecords` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
@ -334,3 +381,259 @@ func (c *Contract) UpdateSOATransaction(name string, email string, refresh *big.
func (c *Contract) UpdateSOAUnsigned(name string, email string, refresh *big.Int, retry *big.Int, expire *big.Int, ttl *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "updateSOA", nil, name, email, refresh, retry, expire, ttl)
}
// RegisterDomainEventsFromApplicationLog retrieves a set of all emitted events
// with "RegisterDomain" name from the provided [result.ApplicationLog].
func RegisterDomainEventsFromApplicationLog(log *result.ApplicationLog) ([]*RegisterDomainEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*RegisterDomainEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "RegisterDomain" {
continue
}
event := new(RegisterDomainEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize RegisterDomainEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to RegisterDomainEvent or
// returns an error if it's not possible to do to so.
func (e *RegisterDomainEvent) 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) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
return nil
}
// AddRecordEventsFromApplicationLog retrieves a set of all emitted events
// with "AddRecord" name from the provided [result.ApplicationLog].
func AddRecordEventsFromApplicationLog(log *result.ApplicationLog) ([]*AddRecordEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*AddRecordEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "AddRecord" {
continue
}
event := new(AddRecordEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize AddRecordEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to AddRecordEvent or
// returns an error if it's not possible to do to so.
func (e *AddRecordEvent) 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.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
index++
e.Type, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Type: %w", err)
}
return nil
}
// DeleteRecordsEventsFromApplicationLog retrieves a set of all emitted events
// with "DeleteRecords" name from the provided [result.ApplicationLog].
func DeleteRecordsEventsFromApplicationLog(log *result.ApplicationLog) ([]*DeleteRecordsEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*DeleteRecordsEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "DeleteRecords" {
continue
}
event := new(DeleteRecordsEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize DeleteRecordsEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to DeleteRecordsEvent or
// returns an error if it's not possible to do to so.
func (e *DeleteRecordsEvent) 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.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
index++
e.Type, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Type: %w", err)
}
return nil
}
// DeleteDomainEventsFromApplicationLog retrieves a set of all emitted events
// with "DeleteDomain" name from the provided [result.ApplicationLog].
func DeleteDomainEventsFromApplicationLog(log *result.ApplicationLog) ([]*DeleteDomainEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*DeleteDomainEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "DeleteDomain" {
continue
}
event := new(DeleteDomainEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to deserialize DeleteDomainEvent from stackitem (execution #%d, event #%d): %w", i, j, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts provided [stackitem.Array] to DeleteDomainEvent or
// returns an error if it's not possible to do to so.
func (e *DeleteDomainEvent) 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) != 1 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
e.Name, err = func(item stackitem.Item) (string, error) {
b, err := item.TryBytes()
if err != nil {
return "", err
}
if !utf8.Valid(b) {
return "", errors.New("not a UTF-8 string")
}
return string(b), nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Name: %w", err)
}
return nil
}

View file

@ -80,6 +80,20 @@ func (c *ContractReader) IteratorChainsByPrefixExpanded(entity *big.Int, entityN
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "iteratorChainsByPrefix", _numOfIteratorItems, entity, entityName, prefix))
}
// ListChainNames invokes `listChainNames` method of contract.
func (c *ContractReader) ListChainNames(entity *big.Int, entityName string) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "listChainNames", entity, entityName))
}
// ListChainNamesExpanded is similar to ListChainNames (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) ListChainNamesExpanded(entity *big.Int, entityName string, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "listChainNames", _numOfIteratorItems, entity, entityName))
}
// ListChains invokes `listChains` method of contract.
func (c *ContractReader) ListChains(namespace string, container string, name []byte) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.Call(c.hash, "listChains", namespace, container, name))
@ -90,6 +104,20 @@ func (c *ContractReader) ListChainsByPrefix(entity *big.Int, entityName string,
return unwrap.Array(c.invoker.Call(c.hash, "listChainsByPrefix", entity, entityName, prefix))
}
// ListTargets invokes `listTargets` method of contract.
func (c *ContractReader) ListTargets(entity *big.Int) (uuid.UUID, result.Iterator, error) {
return unwrap.SessionIterator(c.invoker.Call(c.hash, "listTargets", entity))
}
// ListTargetsExpanded is similar to ListTargets (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) ListTargetsExpanded(entity *big.Int, _numOfIteratorItems int) ([]stackitem.Item, error) {
return unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "listTargets", _numOfIteratorItems, entity))
}
// Version invokes `version` method of contract.
func (c *ContractReader) Version() (*big.Int, error) {
return unwrap.BigInt(c.invoker.Call(c.hash, "version"))
@ -182,3 +210,25 @@ func (c *Contract) SetAdminTransaction(addr util.Uint160) (*transaction.Transact
func (c *Contract) SetAdminUnsigned(addr util.Uint160) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "setAdmin", nil, addr)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(script []byte, manifest []byte, data any) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", script, manifest, data)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", script, manifest, data)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(script []byte, manifest []byte, data any) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, script, manifest, data)
}

View file

@ -165,6 +165,14 @@ func checkContainerList(t *testing.T, c *neotest.ContractInvoker, expected [][]b
})
}
const (
// default SOA record field values
defaultRefresh = 3600 // 1 hour
defaultRetry = 600 // 10 min
defaultExpire = 3600 * 24 * 365 * 10 // 10 years
defaultTTL = 3600 // 1 hour
)
func TestContainerPut(t *testing.T) {
c, cBal, _ := newContainerInvoker(t)
@ -233,6 +241,63 @@ func TestContainerPut(t *testing.T) {
})
})
t.Run("create global domain", func(t *testing.T) {
ctrNNS := neotest.CompileFile(t, c.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
nnsHash := ctrNNS.Hash
cNNS := c.CommitteeInvoker(nnsHash)
cNNS.Invoke(t, true, "register",
"animals", c.CommitteeHash,
"whateveriwant@world.com", int64(defaultRefresh), int64(defaultRetry), int64(defaultExpire), int64(defaultTTL))
cNNS.Invoke(t, true, "register",
"ns", c.CommitteeHash,
"whateveriwant@world.com", int64(defaultRefresh), int64(defaultRetry), int64(defaultExpire), int64(0))
cNNS.Invoke(t, true, "register",
"poland.ns", c.CommitteeHash,
"whateveriwant@world.com", int64(defaultRefresh), int64(defaultRetry), int64(defaultExpire), int64(0))
cNNS.Invoke(t, true, "register",
"sweden.ns", c.CommitteeHash,
"whateveriwant@world.com", int64(defaultRefresh), int64(defaultRetry), int64(defaultExpire), int64(defaultExpire))
cNNS.Invoke(t, stackitem.Null{}, "addRecord",
"poland.ns", int64(nns.TXT), nns.Cnametgt+"=animals")
cNNS.Invoke(t, stackitem.Null{}, "addRecord", "poland.ns", int64(nns.TXT), "random-record")
cNNS.Invoke(t, stackitem.Null{}, "addRecord", "poland.ns", int64(nns.TXT), "ne-qqq=random-record2")
cNNS.Invoke(t, stackitem.Null{}, "addRecord", "sweden.ns", int64(nns.TXT), nns.Cnametgt+"=animals")
balanceMint(t, cBal, acc, (containerFee+containerAliasFee)*5, []byte{})
cNNS.Invoke(t, true, "isAvailable", "bober.animals")
putArgs := []any{cnt.value, cnt.sig, cnt.pub, cnt.token, "bober", "poland.ns"}
c3 := c.WithSigners(c.Committee, acc)
c3.Invoke(t, stackitem.Null{}, "putNamed", putArgs...)
cNNS.Invoke(t, false, "isAvailable", "bober.animals")
putArgs = []any{cnt.value, cnt.sig, cnt.pub, cnt.token, "bober", "sweden.ns"}
c3.InvokeFail(t, "global domain is already taken", "putNamed", putArgs...)
cNNS.InvokeFail(t, "global domain is already taken", "isAvailable", "bober.poland.ns")
cnt2 := dummyContainer(acc)
cNNS.Invoke(t, true, "isAvailable", "uzik.poland.ns")
putArgs = []any{cnt2.value, cnt2.sig, cnt2.pub, cnt2.token, "uzik", "poland.ns"}
c3.Invoke(t, stackitem.Null{}, "putNamed", putArgs...)
c3.Invoke(t, stackitem.Null{}, "delete", cnt.id[:], cnt.sig, cnt.pub, cnt.token)
cNNS.Invoke(t, true, "isAvailable", "bober.animals")
cNNS.Invoke(t, false, "isAvailable", "bober.poland.ns")
cNNS.InvokeFail(t, "global domain is already taken", "isAvailable", "uzik.poland.ns")
cNNS.Invoke(t, false, "isAvailable", "uzik.animals")
records := stackitem.NewArray([]stackitem.Item{stackitem.NewBuffer([]byte("uzik.poland.ns")), stackitem.NewByteArray([]byte(base58.Encode(cnt2.id[:])))})
cNNS.Invoke(t, records, "resolve", "uzik.animals", int64(nns.TXT))
})
t.Run("gas costs are the same for all containers in block", func(t *testing.T) {
const (
containerPerBlock = 512

View file

@ -10,6 +10,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
"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/neotest"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
@ -54,6 +55,9 @@ func TestNNSRegisterTLD(t *testing.T) {
c.InvokeFail(t, "invalid domain name format", "register",
"0com", c.CommitteeHash,
"email@frostfs.info", refresh, retry, expire, ttl)
c.InvokeFail(t, "invalid fragment '0com'", "register",
"0com", c.CommitteeHash,
"email@frostfs.info", refresh, retry, expire, ttl)
acc := c.NewAccount(t)
cAcc := c.WithSigners(acc)
@ -69,6 +73,12 @@ func TestNNSRegisterTLD(t *testing.T) {
c.InvokeFail(t, "invalid domain name format", "register",
"x", c.CommitteeHash,
"email@frostfs.info", refresh, retry, expire, ttl)
c.InvokeFail(t, "domain name too short", "register",
"x", c.CommitteeHash,
"email@frostfs.info", refresh, retry, expire, ttl)
c.InvokeFail(t, "domain name too long", "register",
getTooLongDomainName(255), c.CommitteeHash,
"email@frostfs.info", refresh, retry, expire, ttl)
})
c.Invoke(t, true, "register",
@ -101,26 +111,43 @@ func TestNNSRegister(t *testing.T) {
c3.InvokeFail(t, "invalid domain name format", "register",
"-testdomain.com", acc.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c3.InvokeFail(t, "invalid fragment '-testdomain'", "register",
"-testdomain.com", acc.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c3.InvokeFail(t, "invalid domain name format", "register",
"testdomain-.com", acc.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c3.InvokeFail(t, "invalid fragment 'testdomain-'", "register",
"testdomain-.com", acc.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c3.Invoke(t, true, "register",
"test-domain.com", acc.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
})
c3.Invoke(t, true, "register",
expected := stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray([]byte("testdomain.com")),
})
tx := c3.Invoke(t, true, "register",
"testdomain.com", acc.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c.CheckTxNotificationEvent(t, tx, -1, state.NotificationEvent{ScriptHash: c.Hash, Name: "RegisterDomain", Item: expected})
b := c.TopBlock(t)
expected := stackitem.NewArray([]stackitem.Item{stackitem.NewBuffer(
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewBuffer(
[]byte(fmt.Sprintf("testdomain.com myemail@frostfs.info %d %d %d %d %d",
b.Timestamp, refresh, retry, expire, ttl)))})
c.Invoke(t, expected, "getRecords", "testdomain.com", int64(nns.SOA))
cAcc := c.WithSigners(acc)
cAcc.Invoke(t, stackitem.Null{}, "addRecord",
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})
cAcc.InvokeFail(t, "record already exists", "addRecord",
"testdomain.com", int64(nns.TXT), "first TXT record")
cAcc.Invoke(t, stackitem.Null{}, "addRecord",
@ -132,16 +159,79 @@ func TestNNSRegister(t *testing.T) {
})
c.Invoke(t, expected, "getRecords", "testdomain.com", int64(nns.TXT))
cAcc.Invoke(t, stackitem.Null{}, "setRecord",
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})
expected = stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray([]byte("replaced first")),
stackitem.NewByteArray([]byte("second TXT record")),
})
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)))})
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))
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)))})
c.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ScriptHash: c.Hash, Name: "DeleteRecords", Item: expected})
expected = stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte("testdomain.com"))})
c.CheckTxNotificationEvent(t, tx, 4, state.NotificationEvent{ScriptHash: c.Hash, Name: "DeleteDomain", Item: expected})
c.InvokeFail(t, "token not found", "getRecords", "testdomain.com", int64(nns.SOA))
}
func TestGlobalDomain(t *testing.T) {
c := newNNSInvoker(t, false)
accTop := c.NewAccount(t)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
c1 := c.WithSigners(c.Committee, accTop)
c1.Invoke(t, true, "register",
"com", accTop.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c1.Invoke(t, true, "register",
"testdomain.com", accTop.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c1.Invoke(t, true, "register",
"globaldomain.com", accTop.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c1.Invoke(t, true, "register",
"domik.testdomain.com", accTop.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c1.Invoke(t, stackitem.Null{}, "addRecord",
"domik.testdomain.com", int64(nns.TXT), "CID")
c.Invoke(t, true, "isAvailable", "domik.globaldomain.com")
c1.Invoke(t, stackitem.Null{}, "addRecord",
"testdomain.com", int64(nns.TXT), nns.Cnametgt+"=globaldomain.com")
c.Invoke(t, true, "isAvailable", "dom.testdomain.com")
c1.Invoke(t, stackitem.Null{}, "addRecord",
"domik.testdomain.com", int64(nns.TXT), "random txt record")
c.Invoke(t, true, "isAvailable", "domik.globaldomain.com")
c1.Invoke(t, true, "register",
"dom.testdomain.com", accTop.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c1.Invoke(t, stackitem.Null{}, "addRecord",
"dom.testdomain.com", int64(nns.TXT), "CID")
c.InvokeFail(t, "global domain is already taken", "isAvailable", "dom.testdomain.com")
}
func TestTLDRecord(t *testing.T) {
c := newNNSInvoker(t, true)
c.Invoke(t, stackitem.Null{}, "addRecord",
@ -167,7 +257,7 @@ func TestNNSRegisterMulti(t *testing.T) {
c1 := c.WithSigners(acc)
t.Run("parent domain is missing", func(t *testing.T) {
msg := "one of the parent domains is not registered"
msg := "domain does not exist or is expired: fs.neo.com"
args[0] = "testnet.fs.neo.com"
c1.InvokeFail(t, msg, "register", args...)
})
@ -342,20 +432,38 @@ func TestNNSIsAvailable(t *testing.T) {
acc := c.NewAccount(t)
c1 := c.WithSigners(c.Committee, acc)
c1.InvokeFail(t, "domain does not exist or is expired: domain.com", "isAvailable", "dom.domain.com")
c1.Invoke(t, true, "register",
"domain.com", acc.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c1.Invoke(t, true, "register",
"globaldomain.com", acc.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c.Invoke(t, false, "isAvailable", "domain.com")
c.Invoke(t, true, "isAvailable", "dom.domain.com")
c.InvokeFail(t, "parent does not exist or is expired", "isAvailable", "dom.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(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c.Invoke(t, false, "isAvailable", "dom.domain.com")
c.Invoke(t, true, "isAvailable", "dom.dom.domain.com")
c1.Invoke(t, stackitem.Null{}, "addRecord",
"dom.domain.com", int64(nns.TXT), nns.Cnametgt+"=globaldomain.com")
c.Invoke(t, true, "isAvailable", "dom.dom.domain.com")
c1.Invoke(t, true, "register",
"dom.globaldomain.com", acc.ScriptHash(),
"myemail@frostfs.info", refresh, retry, expire, ttl)
c.InvokeFail(t, "global domain is already taken", "isAvailable", "dom.dom.domain.com")
c.InvokeFail(t, "domain name too long", "isAvailable", getTooLongDomainName(255))
}
func TestNNSRenew(t *testing.T) {
@ -380,6 +488,7 @@ func TestNNSRenew(t *testing.T) {
{Key: stackitem.Make("expiration"), Value: stackitem.Make(ts)},
})
cAcc.Invoke(t, expected, "properties", "testdomain.com")
c.InvokeFail(t, "domain name too long", "renew", getTooLongDomainName(255))
}
func TestNNSResolve(t *testing.T) {
@ -429,3 +538,11 @@ func TestNNSAndProxy(t *testing.T) {
checkBalance(t, c.CommitteeHash, 1)
})
}
func getTooLongDomainName(max int) (res string) {
for len(res) < max {
res += "dom."
}
res += "com"
return res
}

View file

@ -32,6 +32,9 @@ func newPolicyInvoker(t *testing.T) *neotest.ContractInvoker {
func TestPolicy(t *testing.T) {
e := newPolicyInvoker(t)
checkChainsIteratorByPrefix(t, e, policy.Namespace, "mynamespace", "ingress", [][]byte{})
checkChainsIteratorByPrefix(t, e, policy.Container, "cnr1", "ingress", [][]byte{})
// Policies are opaque to the contract and are just raw bytes to store.
p1 := []byte("chain1")
p2 := []byte("chain2")
@ -39,23 +42,31 @@ func TestPolicy(t *testing.T) {
p33 := []byte("chain33")
e.Invoke(t, stackitem.Null{}, "addChain", policy.Namespace, "mynamespace", "ingress:123", p1)
checkTargets(t, e, policy.Namespace, [][]byte{[]byte("mynamespace")})
checkChains(t, e, "mynamespace", "", "ingress", [][]byte{p1})
checkChains(t, e, "mynamespace", "", "all", nil)
e.Invoke(t, stackitem.Null{}, "addChain", policy.Container, "cnr1", "ingress:myrule2", p2)
checkTargets(t, e, policy.Namespace, [][]byte{[]byte("mynamespace")})
checkTargets(t, e, policy.Container, [][]byte{[]byte("cnr1")})
checkChains(t, e, "mynamespace", "", "ingress", [][]byte{p1}) // Only namespace chains.
checkChains(t, e, "mynamespace", "cnr1", "ingress", [][]byte{p1, p2})
checkChains(t, e, "mynamespace", "cnr1", "all", nil) // No chains attached to 'all'.
checkChains(t, e, "mynamespace", "cnr2", "ingress", [][]byte{p1}) // Only namespace, no chains for the container.
e.Invoke(t, stackitem.Null{}, "addChain", policy.Container, "cnr1", "ingress:myrule3", p3)
checkTargets(t, e, policy.Namespace, [][]byte{[]byte("mynamespace")})
checkTargets(t, e, policy.Container, [][]byte{[]byte("cnr1")})
checkChains(t, e, "mynamespace", "cnr1", "ingress", [][]byte{p1, p2, p3})
e.Invoke(t, stackitem.Null{}, "addChain", policy.Container, "cnr1", "ingress:myrule3", p33)
checkTargets(t, e, policy.Namespace, [][]byte{[]byte("mynamespace")})
checkTargets(t, e, policy.Container, [][]byte{[]byte("cnr1")})
checkChain(t, e, policy.Container, "cnr1", "ingress:myrule3", p33)
checkChains(t, e, "mynamespace", "cnr1", "ingress", [][]byte{p1, p2, p33}) // Override chain.
checkChainsByPrefix(t, e, policy.Container, "cnr1", "", [][]byte{p2, p33})
checkChainsByPrefix(t, e, policy.IAM, "", "", nil)
checkChainKeys(t, e, policy.Container, "cnr1", []string{"ingress:myrule2", "ingress:myrule3"})
checkChainsIteratorByPrefix(t, e, policy.Container, "cnr1", "ingress:myrule3", [][]byte{p33})
checkChainsIteratorByPrefix(t, e, policy.Container, "cnr1", "ingress", [][]byte{p2, p33})
@ -73,6 +84,21 @@ func TestPolicy(t *testing.T) {
// Remove by prefix.
e.Invoke(t, stackitem.Null{}, "removeChainsByPrefix", policy.Container, "cnr1", "ingress")
checkChains(t, e, "mynamespace", "cnr1", "ingress", nil)
// Remove by prefix.
e.Invoke(t, stackitem.Null{}, "removeChainsByPrefix", policy.Container, "cnr1", "ingress")
checkChains(t, e, "mynamespace", "cnr1", "ingress", nil)
checkTargets(t, e, policy.Namespace, [][]byte{})
checkTargets(t, e, policy.Container, [][]byte{})
})
t.Run("add again after removal", func(t *testing.T) {
e.Invoke(t, stackitem.Null{}, "addChain", policy.Namespace, "mynamespace", "ingress:123", p1)
e.Invoke(t, stackitem.Null{}, "addChain", policy.Container, "cnr1", "ingress:myrule3", p33)
checkTargets(t, e, policy.Namespace, [][]byte{[]byte("mynamespace")})
checkTargets(t, e, policy.Container, [][]byte{[]byte("cnr1")})
})
}
@ -126,6 +152,25 @@ func checkChainsIteratorByPrefix(t *testing.T, e *neotest.ContractInvoker, kind
}
}
func checkChainKeys(t *testing.T, e *neotest.ContractInvoker, kind byte, entityName string, expected []string) {
s, err := e.TestInvoke(t, "listChainNames", kind, entityName)
require.NoError(t, err)
if s.Len() == 0 {
t.Fatal("Stack is empty")
}
iteratorItem := s.Pop().Value().(*storage.Iterator)
policys := iteratorToArray(iteratorItem)
require.Equal(t, len(expected), len(policys))
for i := range expected {
bytesPolicy, err := policys[i].TryBytes()
require.NoError(t, err)
require.Equal(t, expected[i], string(bytesPolicy))
}
}
func checksChainsOnStack(t *testing.T, s *vm.Stack, expected [][]byte) {
require.Equal(t, 1, s.Len())
@ -148,3 +193,20 @@ func checkChain(t *testing.T, e *neotest.ContractInvoker, kind byte, entityName,
require.True(t, bytes.Equal(expected, s.Pop().Bytes()))
}
func checkTargets(t *testing.T, e *neotest.ContractInvoker, kind byte, expected [][]byte) {
s, err := e.TestInvoke(t, "listTargets", kind)
require.NoError(t, err)
require.NotEqual(t, 0, s.Len(), "stack is empty")
iteratorItem := s.Pop().Value().(*storage.Iterator)
targets := iteratorToArray(iteratorItem)
require.Equal(t, len(expected), len(targets))
for i := range expected {
bytesTargets, err := targets[i].TryBytes()
require.NoError(t, err)
require.Equal(t, expected[i], bytesTargets)
}
}

View file

@ -4,28 +4,41 @@ import (
"path"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"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"
)
const processingPath = "../processing"
func deployProcessingContract(t *testing.T, e *neotest.Executor, addrFrostFS util.Uint160) util.Uint160 {
func deployProcessingContract(t *testing.T, e *neotest.Executor) util.Uint160 {
c := neotest.CompileFile(t, e.CommitteeHash, processingPath, path.Join(processingPath, "config.yml"))
args := make([]any, 1)
args[0] = addrFrostFS
e.DeployContract(t, c, args)
e.DeployContract(t, c, nil)
return c.Hash
}
func newProcessingInvoker(t *testing.T) (*neotest.ContractInvoker, neotest.Signer) {
frostfsInvoker, irMultiAcc, _ := newFrostFSInvoker(t, 2)
hash := deployProcessingContract(t, frostfsInvoker.Executor, frostfsInvoker.Hash)
func newProcessingInvoker(t *testing.T) (*neotest.ContractInvoker, neotest.SingleSigner) {
e := newExecutor(t)
return frostfsInvoker.CommitteeInvoker(hash), irMultiAcc
acc, err := wallet.NewAccount()
require.NoError(t, err)
sig := neotest.NewSingleSigner(acc)
gasHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Gas)
require.NoError(t, err)
vc := e.CommitteeInvoker(gasHash).WithSigners(e.Validator)
vc.Invoke(t, true, "transfer",
e.Validator.ScriptHash(), sig.ScriptHash(),
int64(10_0000_0000), nil)
hash := deployProcessingContract(t, e)
return e.CommitteeInvoker(hash), sig
}
func TestVerify_Processing(t *testing.T) {
@ -35,6 +48,6 @@ func TestVerify_Processing(t *testing.T) {
cIR := c.WithSigners(irMultiAcc)
cIR.Invoke(t, stackitem.NewBool(true), method)
c.Invoke(t, stackitem.NewBool(false), method)
cIR.Invoke(t, stackitem.NewBool(false), method)
c.Invoke(t, stackitem.NewBool(true), method)
}