From 40ab0627d62407d086b6094abdb5dba132c6c2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Franke?= Date: Thu, 28 Nov 2024 14:21:01 +0100 Subject: [PATCH] Add persistence layer and new storage interface This adds persistence options to RUN-DSP by doing the following: - Bump Go to 1.23.3 - Add new storage interfaces - Create badger implementation for new interfaces - Make fields in contracts and transfer reqs public for storage. - Add some helper methods to contracts and transfer request --- .devcontainer/Dockerfile | 2 +- .github/workflows/test-and-lint.yaml | 8 +- .golangci.yml | 2 +- .pre-commit-config.yaml | 2 +- Dockerfile | 2 +- Dockerfile.debug | 2 +- dsp/persistance/badger/agreement_saver.go | 46 +++++ dsp/persistance/badger/contract_saver.go | 69 ++++++++ dsp/persistance/badger/doc.go | 20 +++ dsp/persistance/badger/locking.go | 129 ++++++++++++++ dsp/persistance/badger/provider.go | 164 ++++++++++++++++++ dsp/persistance/badger/transfer_saver.go | 69 ++++++++ dsp/persistance/interface.go | 86 +++++++++ dsp/statemachine/contract.go | 77 ++++---- dsp/statemachine/contract_messages.go | 4 +- dsp/statemachine/contract_transitions.go | 34 ++-- dsp/statemachine/contractstates_gob.go | 15 ++ dsp/statemachine/transfer_request.go | 76 ++++---- .../transfer_request_transitions.go | 34 ++-- dsp/statemachine/transferrequeststates_gob.go | 15 ++ go.mod | 21 ++- go.sum | 127 ++++++++++++++ 22 files changed, 895 insertions(+), 109 deletions(-) create mode 100644 dsp/persistance/badger/agreement_saver.go create mode 100644 dsp/persistance/badger/contract_saver.go create mode 100644 dsp/persistance/badger/doc.go create mode 100644 dsp/persistance/badger/locking.go create mode 100644 dsp/persistance/badger/provider.go create mode 100644 dsp/persistance/badger/transfer_saver.go create mode 100644 dsp/persistance/interface.go create mode 100644 dsp/statemachine/contractstates_gob.go create mode 100644 dsp/statemachine/transferrequeststates_gob.go diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5347b0d..fb3dc82 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/library/golang:1.22 +FROM docker.io/library/golang:1.23.3 RUN apt-get update && \ # Go tools: diff --git a/.github/workflows/test-and-lint.yaml b/.github/workflows/test-and-lint.yaml index 1a4ce31..6cb4fd2 100644 --- a/.github/workflows/test-and-lint.yaml +++ b/.github/workflows/test-and-lint.yaml @@ -24,7 +24,7 @@ jobs: strategy: matrix: go-version: - - 1.22.5 + - 1.23.3 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 @@ -39,7 +39,7 @@ jobs: strategy: matrix: go-version: - - 1.22.5 + - 1.23.3 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 @@ -56,7 +56,7 @@ jobs: strategy: matrix: go-version: - - 1.22.5 + - 1.23.3 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 @@ -77,7 +77,7 @@ jobs: strategy: matrix: go-version: - - 1.22.5 + - 1.23.3 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 diff --git a/.golangci.yml b/.golangci.yml index b7c3168..f01d7b8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -21,6 +21,7 @@ linters: - asciicheck - cyclop - canonicalheader + - copyloopvar - decorder - dupl - dupword @@ -29,7 +30,6 @@ linters: - errname - errorlint - exhaustive - - exportloopref - fatcontext - forbidigo - forcetypeassert diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8633582..cb2975b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/golangci/golangci-lint - rev: v1.58.2 + rev: v1.62.2 hooks: - id: golangci-lint - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/Dockerfile b/Dockerfile index c82c64d..60cf83a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM docker.io/library/golang:1.22.9 AS builder +FROM docker.io/library/golang:1.23.3 AS builder WORKDIR /app COPY . ./ RUN make build diff --git a/Dockerfile.debug b/Dockerfile.debug index 51b93f3..095904c 100644 --- a/Dockerfile.debug +++ b/Dockerfile.debug @@ -16,7 +16,7 @@ ########################################### # DO NOT USE ANYWHERE NEAR SENSITIVE DATA # ########################################### -FROM docker.io/library/golang:1.22.9 AS builder +FROM docker.io/library/golang:1.23.3 AS builder WORKDIR /app COPY . ./ RUN make debug diff --git a/dsp/persistance/badger/agreement_saver.go b/dsp/persistance/badger/agreement_saver.go new file mode 100644 index 0000000..b9ad2ba --- /dev/null +++ b/dsp/persistance/badger/agreement_saver.go @@ -0,0 +1,46 @@ +// Copyright 2024 go-dataspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package badger + +import ( + "context" + + "github.com/go-dataspace/run-dsp/odrl" + "github.com/google/uuid" +) + +func mkAgreementKey(id string) []byte { + return []byte("odrl-agreement-" + id) +} + +// GetAgreement gets an agreement by ID. +func (sp *StorageProvider) GetAgreement( + ctx context.Context, + id uuid.UUID, +) (*odrl.Agreement, error) { + key := mkAgreementKey(id.String()) + return get[*odrl.Agreement](sp.db, key) +} + +// PutAgreement stores an agreement, but should return an error if the agreement ID already +// exists. +func (sp *StorageProvider) PutAgreement(ctx context.Context, agreement *odrl.Agreement) error { + id, err := uuid.Parse(agreement.ID) + if err != nil { + return err + } + key := mkAgreementKey(id.String()) + return put(sp.db, key, agreement) +} diff --git a/dsp/persistance/badger/contract_saver.go b/dsp/persistance/badger/contract_saver.go new file mode 100644 index 0000000..36f5e5f --- /dev/null +++ b/dsp/persistance/badger/contract_saver.go @@ -0,0 +1,69 @@ +// Copyright 2024 go-dataspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//nolint:dupl // Bare minimum of duplicated code +package badger + +import ( + "context" + "fmt" + + "github.com/go-dataspace/run-dsp/dsp/statemachine" + "github.com/go-dataspace/run-dsp/logging" + "github.com/google/uuid" +) + +// GetContractR gets a contract and sets the read-only property. +// It does not check any locks, as the database transaction already freezes the view. +func (sp *StorageProvider) GetContractR( + ctx context.Context, + pid uuid.UUID, + role statemachine.DataspaceRole, +) (*statemachine.Contract, error) { + key := statemachine.MkContractKey(pid, role) + logger := logging.Extract(ctx).With("pid", pid, "role", role, "key", string(key)) + contract, err := get[*statemachine.Contract](sp.db, key) + if err != nil { + logger.Error("Failed to get contract", "err", err) + return nil, fmt.Errorf("could not get contract: %w", err) + } + contract.SetReadOnly() + return contract, nil +} + +// GetContractRW gets a contract but does NOT set the read-only property, allowing changes to be saved. +// It will try to acquire a lock, and if it can't it will panic. The panic will be replaced once +// RUN-DSP reaches beta, but right now we want contract problems to be extremely visible. +func (sp *StorageProvider) GetContractRW( + ctx context.Context, + pid uuid.UUID, + role statemachine.DataspaceRole, +) (*statemachine.Contract, error) { + key := statemachine.MkContractKey(pid, role) + ctx, _ = logging.InjectLabels(ctx, "type", "contract", "pid", pid, "role", role, "key", string(key)) + return getLocked[*statemachine.Contract](ctx, sp, key) +} + +// PutContract saves a contract to the database. +// If the contract is set to read-only, it will panic as this is a bug in the code. +// It will release the lock after it has saved. +func (sp *StorageProvider) PutContract(ctx context.Context, contract *statemachine.Contract) error { + ctx, _ = logging.InjectLabels( + ctx, + "consumer_pid", contract.ConsumerPID, + "provider_pid", contract.ProviderPID, + "role", contract.Role, + ) + return putUnlock(ctx, sp, contract) +} diff --git a/dsp/persistance/badger/doc.go b/dsp/persistance/badger/doc.go new file mode 100644 index 0000000..6112975 --- /dev/null +++ b/dsp/persistance/badger/doc.go @@ -0,0 +1,20 @@ +// Copyright 2024 go-dataspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package badger contains an implementation of the persistance.StorageProvider interface. +// Badger is a pure-go key-value database, not unlike redis. It is made to be embeddable in +// go applications, and offers both on-disk and in-memory backends. +// +// This is intended to be the default storage backend for RUN-DSP. +package badger diff --git a/dsp/persistance/badger/locking.go b/dsp/persistance/badger/locking.go new file mode 100644 index 0000000..5e4b1b9 --- /dev/null +++ b/dsp/persistance/badger/locking.go @@ -0,0 +1,129 @@ +// Copyright 2024 go-dataspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package badger + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/dgraph-io/badger/v4" + "github.com/go-dataspace/run-dsp/logging" +) + +const ( + lockTTL = 15 * time.Minute + maxWaitTime = 10 * time.Minute + lockCheckTime = 10 * time.Millisecond + + logKey = "lock_key" +) + +type lockKey struct { + k []byte +} + +func newLockKey(key []byte) lockKey { + return lockKey{ + k: append([]byte("lock-"), key...), + } +} + +func (l lockKey) key() []byte { + return l.k +} + +func (l lockKey) String() string { + return string(l.k) +} + +func (sp *StorageProvider) AcquireLock(ctx context.Context, k lockKey) error { + err := sp.waitLock(ctx, k) + if err != nil { + return err + } + return sp.setLock(ctx, k) +} + +func (sp *StorageProvider) ReleaseLock(ctx context.Context, k lockKey) error { + logger := logging.Extract(ctx).With(logKey, k.String()) + return sp.db.Update(func(txn *badger.Txn) error { + logger.Debug("Attempting to release lock") + err := txn.Delete(k.key()) + if err != nil { + logger.Error("Could not release lock", "err", err) + } + return err + }) +} + +func (sp *StorageProvider) isLocked(ctx context.Context, k lockKey) bool { + logger := logging.Extract(ctx).With(logKey, k.String()) + err := sp.db.View(func(txn *badger.Txn) error { + logger.Debug("Checking if lock set") + _, err := txn.Get(k.key()) + return err + }) + if err != nil { + if errors.Is(err, badger.ErrKeyNotFound) { + logger.Debug("No key found, reporting unlocked") + return false + } + logger.Error("Got an error, reporting locked", "err", err) + return true + } + logger.Debug("No error, reporting locked") + return true +} + +func (sp *StorageProvider) setLock(ctx context.Context, k lockKey) error { + logger := logging.Extract(ctx).With("key", k.String()) + err := sp.db.Update(func(txn *badger.Txn) error { + logger.Debug("Setting lock") + entry := badger.NewEntry(k.key(), []byte{1}).WithTTL(lockTTL) + return txn.SetEntry(entry) + }) + if err != nil { + logger.Error("Couldn't set lock", "err", err) + return err + } + logger.Debug("Lock set") + return nil +} + +func (sp *StorageProvider) waitLock(ctx context.Context, k lockKey) error { + logger := logging.Extract(ctx).With("key", k.String()) + ticker := time.NewTicker(lockCheckTime) + defer ticker.Stop() + timer := time.NewTicker(maxWaitTime) + defer timer.Stop() + logger.Debug("Starting to wait for lock") + for { + select { + case <-ticker.C: + if sp.isLocked(ctx, k) { + continue + } + return nil + case <-timer.C: + logger.Error("Timeout reached, exiting with error") + return fmt.Errorf("timed out waiting for lock") + case <-ctx.Done(): + logger.Info("Shutting down waiting for lock") + return fmt.Errorf("context cancelled") + } + } +} diff --git a/dsp/persistance/badger/provider.go b/dsp/persistance/badger/provider.go new file mode 100644 index 0000000..9413df6 --- /dev/null +++ b/dsp/persistance/badger/provider.go @@ -0,0 +1,164 @@ +// Copyright 2024 go-dataspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package badger + +import ( + "bytes" + "context" + "encoding/gob" + "fmt" + "time" + + "github.com/dgraph-io/badger/v4" + "github.com/go-dataspace/run-dsp/logging" +) + +const ( + gcInterval = 5 * time.Minute +) + +type StorageProvider struct { + ctx context.Context + db *badger.DB +} + +type storageKeyGenerator interface { + StorageKey() []byte +} + +type writeController interface { + SetReadOnly() + ReadOnly() bool + storageKeyGenerator +} + +// New returns a new badger storage provider, using an inMemory setup if the boolean is set, +// or it will create/reuse the badger database located in dbPath. +func New(ctx context.Context, inMemory bool, dbPath string) (*StorageProvider, error) { + var opt badger.Options + var dbType string + if inMemory { + opt = badger.DefaultOptions("").WithInMemory(inMemory) + dbType = "memory" + } else { + opt = badger.DefaultOptions(dbPath) + dbType = "disk" + } + ctx, _ = logging.InjectLabels(ctx, + "module", "badger", + "db_type", dbType, + "db_path", dbPath, + ) + db, err := badger.Open(opt) + if err != nil { + return nil, err + } + sp := &StorageProvider{ + ctx: ctx, + db: db, + } + go sp.maintenance() + return sp, nil +} + +// maintenance is a goroutine that runs the badger garbage collection every gcInterval. +func (sp StorageProvider) maintenance() { + logger := logging.Extract(sp.ctx) + logger.Info("Starting database maintenance loop") + ticker := time.NewTicker(gcInterval) + for { + select { + case <-ticker.C: + logger.Info("Garbage collection starting") + err := sp.db.RunValueLogGC(0.7) + if err != nil { + logger.Error("GC not completed cleanly", "err", err) + } + case <-sp.ctx.Done(): + ticker.Stop() + sp.db.Close() + return + } + } +} + +// get is a generic function that gets the bytes from the database, decodes and returns it. +func get[T any](db *badger.DB, key []byte) (T, error) { + var thing T + err := db.View(func(txn *badger.Txn) error { + item, err := txn.Get(key) + if err != nil { + return err + } + return item.Value(func(val []byte) error { + dec := gob.NewDecoder(bytes.NewReader(val)) + return dec.Decode(thing) + }) + }) + return thing, err +} + +// getLocked is a generic function that wraps get in a lock/unlock. +func getLocked[T writeController]( + ctx context.Context, + sp *StorageProvider, + key []byte, +) (T, error) { + logger := logging.Extract(ctx) + logger.Info("Acquiring lock") + if err := sp.AcquireLock(ctx, newLockKey(key)); err != nil { + logger.Error("Could not acquire lock, panicking", "err", err) + panic("Failed to acquire lock") + } + logger.Info("Lock acquired, fetching") + thing, err := get[T](sp.db, key) + if err != nil { + logger.Error("Couldn't fetch from db, unlocking", "err", err) + if lockErr := sp.ReleaseLock(ctx, newLockKey(key)); lockErr != nil { + logger.Error("Failed to unlock, will have to depend on TTL", "err", lockErr) + } + var n T + return n, fmt.Errorf("failed to fetch from db") + } + return thing, nil +} + +func put[T any](db *badger.DB, key []byte, thing T) error { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(thing) + if err != nil { + return fmt.Errorf("could not encode in gob: %w", err) + } + return db.Update(func(txn *badger.Txn) error { + return txn.Set(key, buf.Bytes()) + }) +} + +func putUnlock[T writeController](ctx context.Context, sp *StorageProvider, thing T) error { + tType := fmt.Sprintf("%T", thing) + logger := logging.Extract(ctx).With("type", tType) + if thing.ReadOnly() { + logger.Error("Trying to write a read only entry", "type", tType) + panic("Trying to write a read only entry") + } + key := thing.StorageKey() + if err := put(sp.db, key, thing); err != nil { + logger.Error("Could not save entry, not releasing lock", "err", err) + return err + } + + return sp.ReleaseLock(ctx, newLockKey(key)) +} diff --git a/dsp/persistance/badger/transfer_saver.go b/dsp/persistance/badger/transfer_saver.go new file mode 100644 index 0000000..f3e2ebb --- /dev/null +++ b/dsp/persistance/badger/transfer_saver.go @@ -0,0 +1,69 @@ +// Copyright 2024 go-dataspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//nolint:dupl // Bare minimum of duplicated code +package badger + +import ( + "context" + "fmt" + + "github.com/go-dataspace/run-dsp/dsp/statemachine" + "github.com/go-dataspace/run-dsp/logging" + "github.com/google/uuid" +) + +// GetTransferR gets a transfer and sets the read-only property. +// It does not check any locks, as the database transaction already freezes the view. +func (sp *StorageProvider) GetTransferR( + ctx context.Context, + pid uuid.UUID, + role statemachine.DataspaceRole, +) (*statemachine.TransferRequest, error) { + key := statemachine.MkTransferKey(pid, role) + logger := logging.Extract(ctx).With("pid", pid, "role", role, "key", string(key)) + transfer, err := get[*statemachine.TransferRequest](sp.db, key) + if err != nil { + logger.Error("Failed to get transfer", "err", err) + return nil, fmt.Errorf("could not get transfer %w", err) + } + transfer.SetReadOnly() + return transfer, nil +} + +// GetTransferRW gets a transfer but does NOT set the read-only property, allowing changes to be saved. +// It will try to acquire a lock, and if it can't it will panic. The panic will be replaced once +// RUN-DSP reaches beta, but right now we want transfer problems to be extremely visible. +func (sp *StorageProvider) GetTransferRW( + ctx context.Context, + pid uuid.UUID, + role statemachine.DataspaceRole, +) (*statemachine.TransferRequest, error) { + key := statemachine.MkTransferKey(pid, role) + ctx, _ = logging.InjectLabels(ctx, "type", "transfer", "pid", pid, "role", role, "key", string(key)) + return getLocked[*statemachine.TransferRequest](ctx, sp, key) +} + +// PutTransfer saves a transfer to the database. +// If the transfer is set to read-only, it will panic as this is a bug in the code. +// It will release the lock after it has saved. +func (sp *StorageProvider) PutTransfer(ctx context.Context, transfer *statemachine.TransferRequest) error { + ctx, _ = logging.InjectLabels( + ctx, + "consumer_pid", transfer.ConsumerPID, + "provider_pid", transfer.ProviderPID, + "role", transfer.Role, + ) + return putUnlock(ctx, sp, transfer) +} diff --git a/dsp/persistance/interface.go b/dsp/persistance/interface.go new file mode 100644 index 0000000..bfa9091 --- /dev/null +++ b/dsp/persistance/interface.go @@ -0,0 +1,86 @@ +// Copyright 2024 go-dataspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package persistence contains the storage interfaces for the dataspace code. It also contains +// constants and other shared code for the implementation packages. +package persistance + +import ( + "context" + + "github.com/go-dataspace/run-dsp/dsp/statemachine" + "github.com/go-dataspace/run-dsp/odrl" + "github.com/google/uuid" +) + +// StorageProvider is an interface that combines the *Saver interfaces. +type StorageProvider interface { + ContractSaver + AgreementSaver + TransferSaver +} + +// ContractSaver is an interface for storing/retrieving dataspace contracts. +// It supports both read-only and read/write versions. +// Do note that in this implementation that read-only is enforced at save time, as all contract +// fields are public (for encoding purposes). +// It is up to the implementer to handle locking of contracts for the read/write instances, +// and to error if a read-only contract is being saved. +type ContractSaver interface { + // GetContractR gets a read-only version of a contract. + GetContractR( + ctx context.Context, + pid uuid.UUID, + role statemachine.DataspaceRole, + ) (*statemachine.Contract, error) + // GetContractRW gets a read/write version of a contract. This should set a contract specific + // lock for the requested contract. + GetContractRW( + ctx context.Context, + pid uuid.UUID, + role statemachine.DataspaceRole, + ) (*statemachine.Contract, error) + // PutContract saves a contract, and releases the contract specific lock. If the contract + // is read-only, it will return an error. + PutContract(ctx context.Context, contract *statemachine.Contract) error +} + +// AgreementSaver is an interface for storing/retrieving dataspace agreements. +// This does not have any locking involved as agreements are immutable. +type AgreementSaver interface { + // GetAgreement gets an agreement by ID. + GetAgreement(ctx context.Context, id uuid.UUID) (*odrl.Agreement, error) + // PutAgreement stores an agreement, but should return an error if the agreement ID already + // exists. + PutAgreement(ctx context.Context, agreement *odrl.Agreement) error +} + +// TransferSaver is an interface for storing dataspace transfer request. +// The read/write semantics are the same as those for contracts. +type TransferSaver interface { + // GetTransferR gets a read-only version of a transfer request. + GetTransferR( + ctx context.Context, + pid uuid.UUID, + role statemachine.DataspaceRole, + ) (*statemachine.TransferRequest, error) + // GetTransferRW gets a read/write version of a transfer request. + GetTransferRW( + ctx context.Context, + pid uuid.UUID, + role statemachine.DataspaceRole, + ) (*statemachine.TransferRequest, error) + // PutTransfer saves a transfer. + PutTransfer(ctx context.Context, transfer *statemachine.TransferRequest) error +} diff --git a/dsp/statemachine/contract.go b/dsp/statemachine/contract.go index 8e6f3e5..cbf5a85 100644 --- a/dsp/statemachine/contract.go +++ b/dsp/statemachine/contract.go @@ -18,6 +18,7 @@ import ( "fmt" "net/url" "slices" + "strconv" "github.com/go-dataspace/run-dsp/dsp/shared" "github.com/go-dataspace/run-dsp/odrl" @@ -65,35 +66,47 @@ var validTransitions = map[ContractState][]ContractState{ // Contract represents a contract negotiation. type Contract struct { - providerPID uuid.UUID - consumerPID uuid.UUID - state ContractState - offer odrl.Offer - agreement odrl.Agreement - callback *url.URL - self *url.URL - role DataspaceRole + ProviderPID uuid.UUID + ConsumerPID uuid.UUID + State ContractState + Offer odrl.Offer + Agreement odrl.Agreement + Callback *url.URL + Self *url.URL + Role DataspaceRole initial bool + ro bool } -func (cn *Contract) GetProviderPID() uuid.UUID { return cn.providerPID } -func (cn *Contract) SetProviderPID(u uuid.UUID) { cn.providerPID = u } -func (cn *Contract) GetConsumerPID() uuid.UUID { return cn.consumerPID } -func (cn *Contract) SetConsumerPID(u uuid.UUID) { cn.providerPID = u } -func (cn *Contract) GetState() ContractState { return cn.state } -func (cn *Contract) GetOffer() odrl.Offer { return cn.offer } -func (cn *Contract) GetAgreement() odrl.Agreement { return cn.agreement } -func (cn *Contract) GetRole() DataspaceRole { return cn.role } -func (cn *Contract) GetCallback() *url.URL { return cn.callback } -func (cn *Contract) GetSelf() *url.URL { return cn.self } +func (cn *Contract) GetProviderPID() uuid.UUID { return cn.ProviderPID } +func (cn *Contract) SetProviderPID(u uuid.UUID) { cn.ProviderPID = u } +func (cn *Contract) GetConsumerPID() uuid.UUID { return cn.ConsumerPID } +func (cn *Contract) SetConsumerPID(u uuid.UUID) { cn.ProviderPID = u } +func (cn *Contract) GetState() ContractState { return cn.State } +func (cn *Contract) GetOffer() odrl.Offer { return cn.Offer } +func (cn *Contract) GetAgreement() odrl.Agreement { return cn.Agreement } +func (cn *Contract) GetRole() DataspaceRole { return cn.Role } +func (cn *Contract) GetCallback() *url.URL { return cn.Callback } +func (cn *Contract) GetSelf() *url.URL { return cn.Self } func (cn *Contract) GetContract() *Contract { return cn } +func (cn *Contract) SetReadOnly() { cn.ro = true } +func (cn *Contract) ReadOnly() bool { return cn.ro } + +func (cn *Contract) StorageKey() []byte { + id := cn.ConsumerPID + if cn.Role == DataspaceProvider { + id = cn.ProviderPID + } + return MkTransferKey(id, cn.Role) +} + func (cn *Contract) SetState(state ContractState) error { - if !slices.Contains(validTransitions[cn.state], state) { - return fmt.Errorf("can't transition from %s to %s", cn.state, state) + if !slices.Contains(validTransitions[cn.State], state) { + return fmt.Errorf("can't transition from %s to %s", cn.State, state) } - cn.state = state + cn.State = state return nil } @@ -103,7 +116,7 @@ func (cn *Contract) SetCallback(u string) error { if err != nil { return fmt.Errorf("invalid URL: %w", err) } - cn.callback = nu + cn.Callback = nu return nil } @@ -122,14 +135,14 @@ func (cn *Contract) GetContractNegotiation() shared.ContractNegotiation { // we implement a reconciliation loop. func (cn *Contract) Copy() *Contract { return &Contract{ - providerPID: cn.providerPID, - consumerPID: cn.consumerPID, - state: cn.state, - offer: cn.offer, - agreement: cn.agreement, - callback: mustURL(cn.callback), - self: mustURL(cn.self), - role: cn.role, + ProviderPID: cn.ProviderPID, + ConsumerPID: cn.ConsumerPID, + State: cn.State, + Offer: cn.Offer, + Agreement: cn.Agreement, + Callback: mustURL(cn.Callback), + Self: mustURL(cn.Self), + Role: cn.Role, initial: cn.initial, } } @@ -141,3 +154,7 @@ func mustURL(u *url.URL) *url.URL { } return n } + +func MkContractKey(id uuid.UUID, role DataspaceRole) []byte { + return []byte("contract-" + id.String() + "-" + strconv.Itoa(int(role))) +} diff --git a/dsp/statemachine/contract_messages.go b/dsp/statemachine/contract_messages.go index 3460a23..1365a8e 100644 --- a/dsp/statemachine/contract_messages.go +++ b/dsp/statemachine/contract_messages.go @@ -167,7 +167,7 @@ func sendContractOffer(ctx context.Context, r *Reconciler, c *Contract) (func(), func sendContractAgreement(ctx context.Context, r *Reconciler, c *Contract, a Archiver) (func(), error) { ctx, logger := logging.InjectLabels(ctx, "operation", "sendContractAgreement") - c.agreement = odrl.Agreement{ + c.Agreement = odrl.Agreement{ PolicyClass: odrl.PolicyClass{}, Type: "odrl:Agreement", ID: uuid.New().URN(), @@ -188,7 +188,7 @@ func sendContractAgreement(ctx context.Context, r *Reconciler, c *Contract, a Ar logger.Error("Couldn't validate contract agreement", "err", err) return func() {}, fmt.Errorf("couldn't validate contract agreement: %w", err) } - if err := a.PutAgreement(ctx, &c.agreement); err != nil { + if err := a.PutAgreement(ctx, &c.Agreement); err != nil { logger.Error("Couldn't validate contract agreement", "err", err) return func() {}, fmt.Errorf("couldn't validate contract agreement: %w", err) } diff --git a/dsp/statemachine/contract_transitions.go b/dsp/statemachine/contract_transitions.go index 04a3b70..01d18df 100644 --- a/dsp/statemachine/contract_transitions.go +++ b/dsp/statemachine/contract_transitions.go @@ -108,7 +108,7 @@ func (cn *ContractNegotiationInitial) Recv( logger.Error("could not transition state", "err", err) return ctx, nil, fmt.Errorf("could not set state: %w", err) } - cn.Contract.providerPID = uuid.New() + cn.Contract.ProviderPID = uuid.New() cn.Contract.initial = true if err := cn.a.PutProviderContract(ctx, cn.GetContract()); err != nil { logger.Error("failed to save contract", "err", err) @@ -126,7 +126,7 @@ func (cn *ContractNegotiationInitial) Recv( logger.Error("could not transition state", "err", err) return ctx, nil, fmt.Errorf("could not set state: %w", err) } - cn.Contract.consumerPID = uuid.New() + cn.Contract.ConsumerPID = uuid.New() cn.Contract.initial = true if err := cn.a.PutConsumerContract(ctx, cn.GetContract()); err != nil { logger.Error("failed to save contract", "err", err) @@ -205,7 +205,7 @@ func (cn *ContractNegotiationRequested) Recv( callbackAddress = t.CallbackAddress targetState = ContractStates.OFFERED if ppid, err := uuid.Parse(providerPID); err == nil && cn.GetProviderPID() == emptyUUID { - cn.Contract.providerPID = ppid + cn.Contract.ProviderPID = ppid } ctx, logger = logging.InjectLabels(ctx, "recv_msg_type", fmt.Sprintf("%T", t), @@ -215,7 +215,7 @@ func (cn *ContractNegotiationRequested) Recv( consumerPID = t.ConsumerPID providerPID = t.ProviderPID callbackAddress = t.CallbackAddress - cn.Contract.agreement = t.Agreement + cn.Contract.Agreement = t.Agreement targetState = ContractStates.AGREED ctx, logger = logging.InjectLabels(ctx, "recv_msg_type", fmt.Sprintf("%T", t), @@ -263,7 +263,7 @@ func (cn *ContractNegotiationOffered) Recv( callbackAddress = t.CallbackAddress targetState = ContractStates.REQUESTED if ppid, err := uuid.Parse(consumerPID); err == nil && cn.GetConsumerPID() == emptyUUID { - cn.Contract.consumerPID = ppid + cn.Contract.ConsumerPID = ppid } ctx, logger = logging.InjectLabels(ctx, "recv_msg_type", fmt.Sprintf("%T", t), @@ -325,7 +325,7 @@ func (cn *ContractNegotiationAccepted) Recv( "recv_msg_type", fmt.Sprintf("%T", t), ) logger.Debug("Received message") - cn.agreement = t.Agreement + cn.Agreement = t.Agreement return verifyAndTransform(ctx, cn, t.ProviderPID, t.ConsumerPID, t.CallbackAddress, ContractStates.AGREED) case shared.ContractNegotiationTerminationMessage: return processTermination(ctx, t, cn) @@ -451,13 +451,13 @@ func NewContract( role DataspaceRole, ) (context.Context, ContractNegotiationState, error) { contract := &Contract{ - providerPID: providerPID, - consumerPID: consumerPID, - state: state, - offer: offer, - callback: callback, - self: self, - role: role, + ProviderPID: providerPID, + ConsumerPID: consumerPID, + State: state, + Offer: offer, + Callback: callback, + Self: self, + Role: role, } var err error if role == DataspaceConsumer { @@ -505,7 +505,7 @@ func GetContractNegotiation( "contract_consumerPID", cns.GetConsumerPID().String(), "contract_providerPID", cns.GetProviderPID().String(), "contract_state", cns.GetState().String(), - "contract_role", cns.GetContract().role, + "contract_role", cns.GetContract().Role, ) logger.Debug("Found contract") return ctx, cns @@ -552,7 +552,7 @@ func verifyAndTransform( return ctx, nil, fmt.Errorf("could not set state: %w", err) } - if cn.GetContract().role == DataspaceConsumer { + if cn.GetContract().Role == DataspaceConsumer { err = cn.GetArchiver().PutConsumerContract(ctx, cn.GetContract()) } else { err = cn.GetArchiver().PutProviderContract(ctx, cn.GetContract()) @@ -562,8 +562,8 @@ func verifyAndTransform( return ctx, nil, fmt.Errorf("failed to save contract: %w", err) } - if cn.GetContract().role == DataspaceConsumer && targetState == ContractStates.FINALIZED { - err = cn.GetArchiver().PutAgreement(ctx, &cn.GetContract().Copy().agreement) + if cn.GetContract().Role == DataspaceConsumer && targetState == ContractStates.FINALIZED { + err = cn.GetArchiver().PutAgreement(ctx, &cn.GetContract().Copy().Agreement) if err != nil { logger.Error("Could not set state", "err", err) return ctx, nil, fmt.Errorf("failed to save agreement: %w", err) diff --git a/dsp/statemachine/contractstates_gob.go b/dsp/statemachine/contractstates_gob.go new file mode 100644 index 0000000..d000754 --- /dev/null +++ b/dsp/statemachine/contractstates_gob.go @@ -0,0 +1,15 @@ +package statemachine + +func (p ContractState) GobEncode() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *ContractState) GobDecode(b []byte) error { + newp, err := ParseContractState(b) + if err != nil { + return err + } + + *p = newp + return nil +} diff --git a/dsp/statemachine/transfer_request.go b/dsp/statemachine/transfer_request.go index 33c876d..4df35eb 100644 --- a/dsp/statemachine/transfer_request.go +++ b/dsp/statemachine/transfer_request.go @@ -18,6 +18,7 @@ import ( "fmt" "net/url" "slices" + "strconv" "github.com/go-dataspace/run-dsp/dsp/shared" providerv1 "github.com/go-dataspace/run-dsrpc/gen/go/dsp/v1alpha1" @@ -55,37 +56,50 @@ const ( // TransferRequest represents a transfer request and its state. type TransferRequest struct { - state TransferRequestState - providerPID uuid.UUID - consumerPID uuid.UUID - agreementID uuid.UUID - target string - format string - callback *url.URL - self *url.URL - role DataspaceRole - publishInfo *providerv1.PublishInfo - transferDirection TransferDirection + State TransferRequestState + ProviderPID uuid.UUID + ConsumerPID uuid.UUID + AgreementID uuid.UUID + Target string + Format string + Callback *url.URL + Self *url.URL + Role DataspaceRole + PublishInfo *providerv1.PublishInfo + TransferDirection TransferDirection + + ro bool } -func (tr *TransferRequest) GetProviderPID() uuid.UUID { return tr.providerPID } -func (tr *TransferRequest) GetConsumerPID() uuid.UUID { return tr.consumerPID } -func (tr *TransferRequest) GetAgreementID() uuid.UUID { return tr.agreementID } -func (tr *TransferRequest) GetTarget() string { return tr.target } -func (tr *TransferRequest) GetFormat() string { return tr.format } -func (tr *TransferRequest) GetCallback() *url.URL { return tr.callback } -func (tr *TransferRequest) GetSelf() *url.URL { return tr.self } -func (tr *TransferRequest) GetState() TransferRequestState { return tr.state } -func (tr *TransferRequest) GetRole() DataspaceRole { return tr.role } +func (tr *TransferRequest) GetProviderPID() uuid.UUID { return tr.ProviderPID } +func (tr *TransferRequest) GetConsumerPID() uuid.UUID { return tr.ConsumerPID } +func (tr *TransferRequest) GetAgreementID() uuid.UUID { return tr.AgreementID } +func (tr *TransferRequest) GetTarget() string { return tr.Target } +func (tr *TransferRequest) GetFormat() string { return tr.Format } +func (tr *TransferRequest) GetCallback() *url.URL { return tr.Callback } +func (tr *TransferRequest) GetSelf() *url.URL { return tr.Self } +func (tr *TransferRequest) GetState() TransferRequestState { return tr.State } +func (tr *TransferRequest) GetRole() DataspaceRole { return tr.Role } func (tr *TransferRequest) GetTransferRequest() *TransferRequest { return tr } -func (tr *TransferRequest) GetPublishInfo() *providerv1.PublishInfo { return tr.publishInfo } -func (tr *TransferRequest) GetTransferDirection() TransferDirection { return tr.transferDirection } +func (tr *TransferRequest) GetPublishInfo() *providerv1.PublishInfo { return tr.PublishInfo } +func (tr *TransferRequest) GetTransferDirection() TransferDirection { return tr.TransferDirection } + +func (tr *TransferRequest) SetReadOnly() { tr.ro = true } +func (tr *TransferRequest) ReadOnly() bool { return tr.ro } + +func (tr *TransferRequest) StorageKey() []byte { + id := tr.ConsumerPID + if tr.Role == DataspaceProvider { + id = tr.ProviderPID + } + return MkTransferKey(id, tr.Role) +} func (tr *TransferRequest) SetState(state TransferRequestState) error { - if !slices.Contains(validTransferTransitions[tr.state], state) { - return fmt.Errorf("can't transition from %s to %s", tr.state, state) + if !slices.Contains(validTransferTransitions[tr.State], state) { + return fmt.Errorf("can't transition from %s to %s", tr.State, state) } - tr.state = state + tr.State = state return nil } @@ -93,10 +107,14 @@ func (tr *TransferRequest) GetTransferProcess() shared.TransferProcess { return shared.TransferProcess{ Context: dspaceContext, Type: "dspace:TransferProcess", - ProviderPID: tr.providerPID.URN(), - ConsumerPID: tr.consumerPID.URN(), - State: tr.state.String(), + ProviderPID: tr.ProviderPID.URN(), + ConsumerPID: tr.ConsumerPID.URN(), + State: tr.State.String(), } } -func (tr *TransferRequest) SetProviderPID(id uuid.UUID) { tr.providerPID = id } +func (tr *TransferRequest) SetProviderPID(id uuid.UUID) { tr.ProviderPID = id } + +func MkTransferKey(id uuid.UUID, role DataspaceRole) []byte { + return []byte("transfer-" + id.String() + "-" + strconv.Itoa(int(role))) +} diff --git a/dsp/statemachine/transfer_request_transitions.go b/dsp/statemachine/transfer_request_transitions.go index a357908..8530fbe 100644 --- a/dsp/statemachine/transfer_request_transitions.go +++ b/dsp/statemachine/transfer_request_transitions.go @@ -70,9 +70,9 @@ func (tr *TransferRequestNegotiationInitial) Recv( if err != nil { return nil, fmt.Errorf("could not find target: %w", err) } - tr.providerPID = uuid.New() + tr.ProviderPID = uuid.New() return verifyAndTransformTransfer( - ctx, tr, tr.providerPID.URN(), t.ConsumerPID, TransferRequestStates.TRANSFERREQUESTED) + ctx, tr, tr.ProviderPID.URN(), t.ConsumerPID, TransferRequestStates.TRANSFERREQUESTED) default: return nil, fmt.Errorf("invalid message type") } @@ -97,11 +97,11 @@ func (tr *TransferRequestNegotiationRequested) Recv( if err != nil { return nil, fmt.Errorf("invalid UUID for provider PID: %w", err) } - tr.providerPID = u + tr.ProviderPID = u } - if tr.publishInfo == nil { + if tr.PublishInfo == nil { var err error - tr.publishInfo, err = dataAddressToPublishInfo(t.DataAddress) + tr.PublishInfo, err = dataAddressToPublishInfo(t.DataAddress) if err != nil { return nil, fmt.Errorf("invalid dataAddress supplied: %w", err) } @@ -124,7 +124,7 @@ func (tr *TransferRequestNegotiationRequested) Send(ctx context.Context) (func() if err != nil { return func() {}, err } - tr.publishInfo = resp.PublishInfo + tr.PublishInfo = resp.PublishInfo case DirectionPush: // TODO: Signal provider to start uploading dataset here. return func() {}, fmt.Errorf("push flow: %w", ErrNotImplemented) @@ -245,19 +245,19 @@ func NewTransferRequest( return nil, fmt.Errorf("couldn't parse target URN: %w", err) } traReq := &TransferRequest{ - state: state, - consumerPID: consumerPID, - agreementID: agreementID, - target: targetID, - format: format, - callback: callback, - self: self, - role: role, - publishInfo: publishInfo, - transferDirection: DirectionPush, + State: state, + ConsumerPID: consumerPID, + AgreementID: agreementID, + Target: targetID, + Format: format, + Callback: callback, + Self: self, + Role: role, + PublishInfo: publishInfo, + TransferDirection: DirectionPush, } if publishInfo == nil { - traReq.transferDirection = DirectionPull + traReq.TransferDirection = DirectionPull } if role == DataspaceConsumer { err = store.PutConsumerTransfer(ctx, traReq) diff --git a/dsp/statemachine/transferrequeststates_gob.go b/dsp/statemachine/transferrequeststates_gob.go new file mode 100644 index 0000000..ea2e2e8 --- /dev/null +++ b/dsp/statemachine/transferrequeststates_gob.go @@ -0,0 +1,15 @@ +package statemachine + +func (p TransferRequestState) GobEncode() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *TransferRequestState) GobDecode(b []byte) error { + newp, err := ParseTransferRequestState(b) + if err != nil { + return err + } + + *p = newp + return nil +} diff --git a/go.mod b/go.mod index 5015449..e226108 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/go-dataspace/run-dsp -go 1.22.9 +go 1.23.3 require ( github.com/alecthomas/chroma/v2 v2.14.0 @@ -21,20 +21,30 @@ require ( ) require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dgraph-io/badger/v4 v4.4.0 // indirect + github.com/dgraph-io/ristretto/v2 v2.0.0 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.4 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/flatbuffers v24.3.25+incompatible // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -44,15 +54,16 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect + go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.28.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index b64a3f6..ce796d4 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,33 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v4 v4.4.0 h1:rA48XiDynZLyMdlaJl67p9+lqfqwxlgKtCpYLAio7Zk= +github.com/dgraph-io/badger/v4 v4.4.0/go.mod h1:sONMmPPfbnj9FPwS/etCqky/ULth6CQJuAZSuWCmixE= +github.com/dgraph-io/ristretto/v2 v2.0.0 h1:l0yiSOtlJvc0otkqyMaDNysg8E9/F/TYZwMbxscNOAQ= +github.com/dgraph-io/ristretto/v2 v2.0.0/go.mod h1:FVFokF2dRqXyPyeMnK1YDy8Fc6aTe0IKgbcd03CYeEk= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +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/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -31,8 +48,35 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +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.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= +github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +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.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= @@ -45,6 +89,10 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -64,9 +112,12 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -96,11 +147,16 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= @@ -109,22 +165,91 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +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.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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/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/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-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +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/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +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.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -135,3 +260,5 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=