diff --git a/.circleci/config.yml b/.circleci/config.yml index a1e3e1fce..4b32f3206 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,14 +1,14 @@ version: 2.1 orbs: - go: circleci/go@1.7.3 + go: circleci/go@1.12.0 slack: circleci/slack@4.7.1 codecov: codecov/codecov@3.2.4 parameters: ubuntu_image: type: string - default: "ubuntu-2204:2022.04.2" + default: "ubuntu-2404:2024.05.1" workflows: version: 2 @@ -18,7 +18,7 @@ workflows: name: test_with_go_<< matrix.go_version >> matrix: &go-version-matrix parameters: - go_version: ["1.21.10"] + go_version: ["1.23.3"] circleci_build_and_test_nightly: triggers: @@ -33,7 +33,7 @@ workflows: context: lamprey-secrets matrix: &go-version-matrix parameters: - go_version: ["1.21.10"] + go_version: ["1.23.3"] - indexer_vs_algod_nightly: name: nightly_test_indexer_vs_algod context: lamprey-secrets @@ -135,7 +135,7 @@ commands: steps: - run: name: Install golangci-lint - command: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.2 + command: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0 run_e2e_tests: steps: diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 1677c7cb1..05801edab 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -12,13 +12,13 @@ jobs: goreleaser: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.5.3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - run: git fetch --force --tags - name: go dependency - uses: actions/setup-go@v4.0.1 + uses: actions/setup-go@v5 with: go-version-file: 'go.mod' diff --git a/.github/workflows/pr-type-category.yml b/.github/workflows/pr-type-category.yml index c33fce940..ed2983321 100644 --- a/.github/workflows/pr-type-category.yml +++ b/.github/workflows/pr-type-category.yml @@ -10,7 +10,7 @@ jobs: name: Check PR Category and Type steps: - name: Checking for correct number of required github pr labels - uses: mheap/github-action-required-labels@v2 + uses: mheap/github-action-required-labels@v5 with: mode: exactly count: 1 diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml index 914c95eff..5e244805e 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/reviewdog.yml @@ -7,18 +7,18 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code into the Go module directory - uses: actions/checkout@v3.5.3 + uses: actions/checkout@v4 with: fetch-depth: 0 # required for new-from-rev option in .golangci.yml - name: Install specific golang - uses: actions/setup-go@v4.0.1 + uses: actions/setup-go@v5 with: - go-version: '1.21.10' + go-version: '1.23.3' - name: reviewdog-golangci-lint - uses: reviewdog/action-golangci-lint@v2.6.1 + uses: reviewdog/action-golangci-lint@v2.6.2 with: go_version_file: go.mod - golangci_lint_version: "v1.58.0" + golangci_lint_version: "v1.62.0" golangci_lint_flags: "-c .golangci.yml --allow-parallel-runners" reporter: "github-pr-check" tool_name: "Lint Errors" @@ -30,18 +30,18 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code into the Go module directory - uses: actions/checkout@v3.5.3 + uses: actions/checkout@v4 with: fetch-depth: 0 # required for new-from-rev option in .golangci.yml - name: Install specific golang - uses: actions/setup-go@v4.0.1 + uses: actions/setup-go@v5 with: - go-version: '1.21.10' + go-version: '1.23.3' - name: reviewdog-golangci-lint - uses: reviewdog/action-golangci-lint@v2.6.1 + uses: reviewdog/action-golangci-lint@v2.6.2 with: go_version_file: go.mod - golangci_lint_version: "v1.58.0" + golangci_lint_version: "v1.62.0" golangci_lint_flags: "-c .golangci-warnings.yml --allow-parallel-runners" reporter: "github-pr-check" tool_name: "Lint Warnings" diff --git a/.golangci.yml b/.golangci.yml index 1854949cd..c9ccc8849 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,6 @@ run: timeout: 5m - tests: false + tests: true linters: disable-all: true @@ -66,4 +66,4 @@ issues: # revive: ignore some rules - "^unused-parameter: parameter" - - "^package-comments: should have a package comment" \ No newline at end of file + - "^package-comments: should have a package comment" diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index 5ffa97e9d..000000000 --- a/CODEOWNERS +++ /dev/null @@ -1,2 +0,0 @@ -.github/ @algorand/lamprey -.circleci/ @algorand/lamprey diff --git a/Makefile b/Makefile index 6a46da177..b937929bd 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ idb/postgres/internal/schema/setup_postgres_sql.go: idb/postgres/internal/schema cd idb/postgres/internal/schema && go generate idb/mocks/IndexerDb.go: idb/idb.go - go install github.com/vektra/mockery/v2@v2.20.0 + go install github.com/vektra/mockery/v2@v2.47.0 cd idb && mockery --name=IndexerDb # check that all packages (except tests) compile diff --git a/accounting/rewind.go b/accounting/rewind.go new file mode 100644 index 000000000..ea2397a4f --- /dev/null +++ b/accounting/rewind.go @@ -0,0 +1,178 @@ +package accounting + +import ( + "context" + "fmt" + + models "github.com/algorand/indexer/v3/api/generated/v2" + "github.com/algorand/indexer/v3/idb" + "github.com/algorand/indexer/v3/types" + + sdk "github.com/algorand/go-algorand-sdk/v2/types" +) + +// ConsistencyError is returned when the database returns inconsistent (stale) results. +type ConsistencyError struct { + msg string +} + +func (e ConsistencyError) Error() string { + return e.msg +} +func assetUpdate(account *models.Account, assetid uint64, add, sub uint64) { + if account.Assets == nil { + account.Assets = new([]models.AssetHolding) + } + assets := *account.Assets + for i, ah := range assets { + if ah.AssetId == assetid { + ah.Amount += add + ah.Amount -= sub + assets[i] = ah + // found and updated asset, done + return + } + } + // add asset to list + assets = append(assets, models.AssetHolding{ + Amount: add - sub, + AssetId: assetid, + //Creator: base32 addr string of asset creator, TODO + //IsFrozen: leave nil? // TODO: on close record frozen state for rewind + }) + *account.Assets = assets +} + +// SpecialAccountRewindError indicates that an attempt was made to rewind one of the special accounts. +type SpecialAccountRewindError struct { + account string +} + +// MakeSpecialAccountRewindError helper to initialize a SpecialAccountRewindError. +func MakeSpecialAccountRewindError(account string) *SpecialAccountRewindError { + return &SpecialAccountRewindError{account: account} +} + +// Error is part of the error interface. +func (sare *SpecialAccountRewindError) Error() string { + return fmt.Sprintf("unable to rewind the %s", sare.account) +} + +var specialAccounts *types.SpecialAddresses + +// AccountAtRound queries the idb.IndexerDb object for transactions and rewinds most fields of the account back to +// their values at the requested round. +// `round` must be <= `account.Round` +func AccountAtRound(ctx context.Context, account models.Account, round uint64, db idb.IndexerDb) (acct models.Account, err error) { + // Make sure special accounts cache has been initialized. + if specialAccounts == nil { + var accounts types.SpecialAddresses + accounts, err = db.GetSpecialAccounts(ctx) + if err != nil { + return models.Account{}, fmt.Errorf("unable to get special accounts: %v", err) + } + specialAccounts = &accounts + } + acct = account + var addr sdk.Address + addr, err = sdk.DecodeAddress(account.Address) + if err != nil { + return + } + // ensure that the don't attempt to rewind a special account. + if specialAccounts.FeeSink == addr { + err = MakeSpecialAccountRewindError("FeeSink") + return + } + if specialAccounts.RewardsPool == addr { + err = MakeSpecialAccountRewindError("RewardsPool") + return + } + // Get transactions and rewind account. + tf := idb.TransactionFilter{ + Address: addr[:], + MinRound: round + 1, + MaxRound: account.Round, + } + ctx2, cf := context.WithCancel(ctx) + // In case of a panic before the next defer, call cf() here. + defer cf() + txns, r := db.Transactions(ctx2, tf) + // In case of an error, make sure the context is cancelled, and the channel is cleaned up. + defer func() { + cf() + for range txns { + } + }() + if r < account.Round { + err = ConsistencyError{fmt.Sprintf("queried round r: %d < account.Round: %d", r, account.Round)} + return + } + txcount := 0 + for txnrow := range txns { + if txnrow.Error != nil { + err = txnrow.Error + return + } + txcount++ + stxn := txnrow.Txn + if stxn == nil { + return models.Account{}, + fmt.Errorf("rewinding past inner transactions is not supported") + } + if addr == stxn.Txn.Sender { + acct.AmountWithoutPendingRewards += uint64(stxn.Txn.Fee) + acct.AmountWithoutPendingRewards -= uint64(stxn.SenderRewards) + } + switch stxn.Txn.Type { + case sdk.PaymentTx: + if addr == stxn.Txn.Sender { + acct.AmountWithoutPendingRewards += uint64(stxn.Txn.Amount) + } + if addr == stxn.Txn.Receiver { + acct.AmountWithoutPendingRewards -= uint64(stxn.Txn.Amount) + acct.AmountWithoutPendingRewards -= uint64(stxn.ReceiverRewards) + } + if addr == stxn.Txn.CloseRemainderTo { + // unwind receiving a close-to + acct.AmountWithoutPendingRewards -= uint64(stxn.ClosingAmount) + acct.AmountWithoutPendingRewards -= uint64(stxn.CloseRewards) + } else if !stxn.Txn.CloseRemainderTo.IsZero() { + // unwind sending a close-to + acct.AmountWithoutPendingRewards += uint64(stxn.ClosingAmount) + } + case sdk.KeyRegistrationTx: + // TODO: keyreg does not rewind. workaround: query for txns on an account with typeenum=2 to find previous values it was set to. + case sdk.AssetConfigTx: + if stxn.Txn.ConfigAsset == 0 { + // create asset, unwind the application of the value + assetUpdate(&acct, txnrow.AssetID, 0, stxn.Txn.AssetParams.Total) + } + case sdk.AssetTransferTx: + if addr == stxn.Txn.AssetSender || addr == stxn.Txn.Sender { + assetUpdate(&acct, uint64(stxn.Txn.XferAsset), stxn.Txn.AssetAmount+txnrow.Extra.AssetCloseAmount, 0) + } + if addr == stxn.Txn.AssetReceiver { + assetUpdate(&acct, uint64(stxn.Txn.XferAsset), 0, stxn.Txn.AssetAmount) + } + if addr == stxn.Txn.AssetCloseTo { + assetUpdate(&acct, uint64(stxn.Txn.XferAsset), 0, txnrow.Extra.AssetCloseAmount) + } + case sdk.AssetFreezeTx: + default: + err = fmt.Errorf("%s[%d,%d]: rewinding past txn type %s is not currently supported", account.Address, txnrow.Round, txnrow.Intra, stxn.Txn.Type) + return + } + } + acct.Round = round + // Due to accounts being closed and re-opened, we cannot always rewind Rewards. So clear it out. + acct.Rewards = 0 + // Computing pending rewards is not supported. + acct.PendingRewards = 0 + acct.Amount = acct.AmountWithoutPendingRewards + // MinBalance is not supported. + acct.MinBalance = 0 + // TODO: Clear out the closed-at field as well. Like Rewards we cannot know this value for all accounts. + //acct.ClosedAt = 0 + return +} diff --git a/api/README.md b/api/README.md index 3e2f2f2db..23c35b22f 100644 --- a/api/README.md +++ b/api/README.md @@ -9,7 +9,7 @@ The API is defined using [OpenAPI v2](https://swagger.io/specification/v2/) in * The Makefile will install our fork of **oapi-codegen**, use `make oapi-codegen` to install it directly. 1. Document your changes by editing **indexer.oas2.yml** -2. Regenerate the endpoints by running **generate.sh**. The sources at **generated/** will be updated. +2. Regenerate the endpoints by running **make generate** from the `api` directory. The sources at **generated/** will be updated. 3. Update the implementation in **handlers.go**. It is sometimes useful to consult **generated/routes.go** to make sure the handler properly implements **ServerInterface**. ## What codegen tool is used? diff --git a/api/app_boxes_fixtures_test.go b/api/app_boxes_fixtures_test.go index 8d067b25e..df05e2f38 100644 --- a/api/app_boxes_fixtures_test.go +++ b/api/app_boxes_fixtures_test.go @@ -6,13 +6,14 @@ import ( "fmt" "testing" - "github.com/algorand/indexer/v3/idb/postgres" "github.com/stretchr/testify/require" "github.com/algorand/avm-abi/apps" + "github.com/algorand/indexer/v3/idb/postgres" + "github.com/algorand/indexer/v3/util/test" + "github.com/algorand/go-algorand-sdk/v2/crypto" sdk "github.com/algorand/go-algorand-sdk/v2/types" - "github.com/algorand/indexer/v3/util/test" ) func goalEncode(t *testing.T, s string) string { @@ -23,7 +24,7 @@ func goalEncode(t *testing.T, s string) string { return string(b2) } -var goalEncodingExamples map[string]string = map[string]string{ +var goalEncodingExamples = map[string]string{ "str": "str", "string": "string", "int": "42", diff --git a/api/converter_utils.go b/api/converter_utils.go index ed53e5891..b16619662 100644 --- a/api/converter_utils.go +++ b/api/converter_utils.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "errors" "fmt" + "slices" "sort" "strconv" "strings" @@ -34,28 +35,6 @@ func decodeDigest(str *string, field string, errorArr []string) (string, []strin return "", errorArr } -// decodeSdkAddress returns the sdk.Address representation of the input string, or appends an error to errorArr -func decodeSdkAddress(str string, field string, errorArr []string) (sdk.Address, []string) { - addr, err := sdk.DecodeAddress(str) - if err != nil { - return sdk.ZeroAddress, append(errorArr, fmt.Sprintf("%s '%s': %v", errUnableToParseAddress, field, err)) - } - return addr, errorArr -} - -// decodeAddress returns the byte representation of the input string, or appends an error to errorArr -func decodeAddress(str *string, field string, errorArr []string) ([]byte, []string) { - if str != nil { - addr, err := sdk.DecodeAddress(*str) - if err != nil { - return nil, append(errorArr, fmt.Sprintf("%s '%s': %v", errUnableToParseAddress, field, err)) - } - return addr[:], errorArr - } - // Pass through - return nil, errorArr -} - // decodeAddress converts the role information into a bitmask, or appends an error to errorArr func decodeAddressRole(role *string, excludeCloseTo *bool, errorArr []string) (idb.AddressRole, []string) { // If the string is nil, return early. @@ -267,95 +246,6 @@ type rowData struct { AssetCloseAmount uint64 } -// rowToBlock parses the idb.BlockRow and generates the appropriate generated.Block object. -func rowToBlock(blockHeader *sdk.BlockHeader) generated.Block { - - rewards := generated.BlockRewards{ - FeeSink: blockHeader.FeeSink.String(), - RewardsCalculationRound: uint64(blockHeader.RewardsRecalculationRound), - RewardsLevel: blockHeader.RewardsLevel, - RewardsPool: blockHeader.RewardsPool.String(), - RewardsRate: blockHeader.RewardsRate, - RewardsResidue: blockHeader.RewardsResidue, - } - - upgradeState := generated.BlockUpgradeState{ - CurrentProtocol: string(blockHeader.CurrentProtocol), - NextProtocol: strPtr(string(blockHeader.NextProtocol)), - NextProtocolApprovals: uint64Ptr(blockHeader.NextProtocolApprovals), - NextProtocolSwitchOn: uint64Ptr(uint64(blockHeader.NextProtocolSwitchOn)), - NextProtocolVoteBefore: uint64Ptr(uint64(blockHeader.NextProtocolVoteBefore)), - } - - upgradeVote := generated.BlockUpgradeVote{ - UpgradeApprove: boolPtr(blockHeader.UpgradeApprove), - UpgradeDelay: uint64Ptr(uint64(blockHeader.UpgradeDelay)), - UpgradePropose: strPtr(string(blockHeader.UpgradePropose)), - } - - var partUpdates *generated.ParticipationUpdates = &generated.ParticipationUpdates{} - if len(blockHeader.ExpiredParticipationAccounts) > 0 { - addrs := make([]string, len(blockHeader.ExpiredParticipationAccounts)) - for i := 0; i < len(addrs); i++ { - addrs[i] = blockHeader.ExpiredParticipationAccounts[i].String() - } - partUpdates.ExpiredParticipationAccounts = strArrayPtr(addrs) - } - if len(blockHeader.AbsentParticipationAccounts) > 0 { - addrs := make([]string, len(blockHeader.AbsentParticipationAccounts)) - for i := 0; i < len(addrs); i++ { - addrs[i] = blockHeader.AbsentParticipationAccounts[i].String() - } - partUpdates.AbsentParticipationAccounts = strArrayPtr(addrs) - } - if *partUpdates == (generated.ParticipationUpdates{}) { - partUpdates = nil - } - - // order these so they're deterministic - orderedTrackingTypes := make([]sdk.StateProofType, len(blockHeader.StateProofTracking)) - trackingArray := make([]generated.StateProofTracking, len(blockHeader.StateProofTracking)) - elems := 0 - for key := range blockHeader.StateProofTracking { - orderedTrackingTypes[elems] = key - elems++ - } - sort.Slice(orderedTrackingTypes, func(i, j int) bool { return orderedTrackingTypes[i] < orderedTrackingTypes[j] }) - for i := 0; i < len(orderedTrackingTypes); i++ { - stpfTracking := blockHeader.StateProofTracking[orderedTrackingTypes[i]] - thing1 := generated.StateProofTracking{ - NextRound: uint64Ptr(uint64(stpfTracking.StateProofNextRound)), - Type: uint64Ptr(uint64(orderedTrackingTypes[i])), - VotersCommitment: byteSliceOmitZeroPtr(stpfTracking.StateProofVotersCommitment), - OnlineTotalWeight: uint64Ptr(uint64(stpfTracking.StateProofOnlineTotalWeight)), - } - trackingArray[orderedTrackingTypes[i]] = thing1 - } - - ret := generated.Block{ - Bonus: uint64PtrOrNil(uint64(blockHeader.Bonus)), - FeesCollected: uint64PtrOrNil(uint64(blockHeader.FeesCollected)), - GenesisHash: blockHeader.GenesisHash[:], - GenesisId: blockHeader.GenesisID, - ParticipationUpdates: partUpdates, - PreviousBlockHash: blockHeader.Branch[:], - Proposer: addrPtr(blockHeader.Proposer), - ProposerPayout: uint64PtrOrNil(uint64(blockHeader.ProposerPayout)), - Rewards: &rewards, - Round: uint64(blockHeader.Round), - Seed: blockHeader.Seed[:], - StateProofTracking: &trackingArray, - Timestamp: uint64(blockHeader.TimeStamp), - Transactions: nil, - TransactionsRoot: blockHeader.TxnCommitments.NativeSha512_256Commitment[:], - TransactionsRootSha256: blockHeader.TxnCommitments.Sha256Commitment[:], - TxnCounter: uint64Ptr(blockHeader.TxnCounter), - UpgradeState: &upgradeState, - UpgradeVote: &upgradeVote, - } - return ret -} - // txnRowToTransaction parses the idb.TxnRow and generates the appropriate generated.Transaction object. // If the TxnRow contains a RootTxn, the generated.Transaction object will be the root txn. func txnRowToTransaction(row idb.TxnRow) (generated.Transaction, error) { @@ -410,6 +300,93 @@ func txnRowToTransaction(row idb.TxnRow) (generated.Transaction, error) { return txn, nil } +func hdrRowToBlock(row idb.BlockRow) generated.BlockHeader { + + rewards := generated.BlockRewards{ + FeeSink: row.BlockHeader.FeeSink.String(), + RewardsCalculationRound: uint64(row.BlockHeader.RewardsRecalculationRound), + RewardsLevel: row.BlockHeader.RewardsLevel, + RewardsPool: row.BlockHeader.RewardsPool.String(), + RewardsRate: row.BlockHeader.RewardsRate, + RewardsResidue: row.BlockHeader.RewardsResidue, + } + + upgradeState := generated.BlockUpgradeState{ + CurrentProtocol: string(row.BlockHeader.CurrentProtocol), + NextProtocol: strPtr(string(row.BlockHeader.NextProtocol)), + NextProtocolApprovals: uint64Ptr(row.BlockHeader.NextProtocolApprovals), + NextProtocolSwitchOn: uint64Ptr(uint64(row.BlockHeader.NextProtocolSwitchOn)), + NextProtocolVoteBefore: uint64Ptr(uint64(row.BlockHeader.NextProtocolVoteBefore)), + } + + upgradeVote := generated.BlockUpgradeVote{ + UpgradeApprove: boolPtr(row.BlockHeader.UpgradeApprove), + UpgradeDelay: uint64Ptr(uint64(row.BlockHeader.UpgradeDelay)), + UpgradePropose: strPtr(string(row.BlockHeader.UpgradePropose)), + } + + var partUpdates *generated.ParticipationUpdates = &generated.ParticipationUpdates{} + if len(row.BlockHeader.ExpiredParticipationAccounts) > 0 { + addrs := make([]string, len(row.BlockHeader.ExpiredParticipationAccounts)) + for i := 0; i < len(addrs); i++ { + addrs[i] = row.BlockHeader.ExpiredParticipationAccounts[i].String() + } + partUpdates.ExpiredParticipationAccounts = strArrayPtr(addrs) + } + if len(row.BlockHeader.AbsentParticipationAccounts) > 0 { + addrs := make([]string, len(row.BlockHeader.AbsentParticipationAccounts)) + for i := 0; i < len(addrs); i++ { + addrs[i] = row.BlockHeader.AbsentParticipationAccounts[i].String() + } + partUpdates.AbsentParticipationAccounts = strArrayPtr(addrs) + } + if *partUpdates == (generated.ParticipationUpdates{}) { + partUpdates = nil + } + + // order these so they're deterministic + orderedTrackingTypes := make([]sdk.StateProofType, len(row.BlockHeader.StateProofTracking)) + trackingArray := make([]generated.StateProofTracking, len(row.BlockHeader.StateProofTracking)) + elems := 0 + for key := range row.BlockHeader.StateProofTracking { + orderedTrackingTypes[elems] = key + elems++ + } + slices.Sort(orderedTrackingTypes) + for i := 0; i < len(orderedTrackingTypes); i++ { + stpfTracking := row.BlockHeader.StateProofTracking[orderedTrackingTypes[i]] + thing1 := generated.StateProofTracking{ + NextRound: uint64Ptr(uint64(stpfTracking.StateProofNextRound)), + Type: uint64Ptr(uint64(orderedTrackingTypes[i])), + VotersCommitment: byteSliceOmitZeroPtr(stpfTracking.StateProofVotersCommitment), + OnlineTotalWeight: uint64Ptr(uint64(stpfTracking.StateProofOnlineTotalWeight)), + } + trackingArray[orderedTrackingTypes[i]] = thing1 + } + + ret := generated.BlockHeader{ + Bonus: uint64PtrOrNil(uint64(row.BlockHeader.Bonus)), + FeesCollected: uint64PtrOrNil(uint64(row.BlockHeader.FeesCollected)), + GenesisHash: row.BlockHeader.GenesisHash[:], + GenesisId: row.BlockHeader.GenesisID, + ParticipationUpdates: partUpdates, + PreviousBlockHash: row.BlockHeader.Branch[:], + Proposer: addrPtr(row.BlockHeader.Proposer), + ProposerPayout: uint64PtrOrNil(uint64(row.BlockHeader.ProposerPayout)), + Rewards: &rewards, + Round: uint64(row.BlockHeader.Round), + Seed: row.BlockHeader.Seed[:], + StateProofTracking: &trackingArray, + Timestamp: uint64(row.BlockHeader.TimeStamp), + TransactionsRoot: row.BlockHeader.TxnCommitments.NativeSha512_256Commitment[:], + TransactionsRootSha256: row.BlockHeader.TxnCommitments.Sha256Commitment[:], + TxnCounter: uint64Ptr(row.BlockHeader.TxnCounter), + UpgradeState: &upgradeState, + UpgradeVote: &upgradeVote, + } + return ret +} + func signedTxnWithAdToTransaction(stxn *sdk.SignedTxnWithAD, extra rowData) (generated.Transaction, error) { var payment *generated.TransactionPayment var keyreg *generated.TransactionKeyreg @@ -418,6 +395,7 @@ func signedTxnWithAdToTransaction(stxn *sdk.SignedTxnWithAD, extra rowData) (gen var assetTransfer *generated.TransactionAssetTransfer var application *generated.TransactionApplication var stateProof *generated.TransactionStateProof + var heartbeat *generated.TransactionHeartbeat switch stxn.Txn.Type { case sdk.PaymentTx: @@ -621,6 +599,22 @@ func signedTxnWithAdToTransaction(stxn *sdk.SignedTxnWithAD, extra rowData) (gen StateProofType: uint64Ptr(uint64(stxn.Txn.StateProofType)), } stateProof = &proofTxn + case sdk.HeartbeatTx: + hb := stxn.Txn.HeartbeatTxnFields + hbTxn := generated.TransactionHeartbeat{ + HbAddress: hb.HbAddress.String(), + HbKeyDilution: hb.HbKeyDilution, + HbProof: generated.HbProofFields{ + HbPk: byteSliceOmitZeroPtr(hb.HbProof.PK[:]), + HbPk1sig: byteSliceOmitZeroPtr(hb.HbProof.PK1Sig[:]), + HbPk2: byteSliceOmitZeroPtr(hb.HbProof.PK2[:]), + HbPk2sig: byteSliceOmitZeroPtr(hb.HbProof.PK2Sig[:]), + HbSig: byteSliceOmitZeroPtr(hb.HbProof.Sig[:]), + }, + HbSeed: hb.HbSeed[:], + HbVoteId: hb.HbVoteID[:], + } + heartbeat = &hbTxn } var localStateDelta *[]generated.AccountStateDelta @@ -671,9 +665,9 @@ func signedTxnWithAdToTransaction(stxn *sdk.SignedTxnWithAD, extra rowData) (gen for _, t := range stxn.ApplyData.EvalDelta.InnerTxns { extra2 := extra if t.Txn.Type == sdk.ApplicationCallTx { - extra2.AssetID = uint64(t.ApplyData.ApplicationID) + extra2.AssetID = t.ApplyData.ApplicationID } else if t.Txn.Type == sdk.AssetConfigTx { - extra2.AssetID = uint64(t.ApplyData.ConfigAsset) + extra2.AssetID = t.ApplyData.ConfigAsset } else { extra2.AssetID = 0 } @@ -697,6 +691,7 @@ func signedTxnWithAdToTransaction(stxn *sdk.SignedTxnWithAD, extra rowData) (gen PaymentTransaction: payment, KeyregTransaction: keyreg, StateProofTransaction: stateProof, + HeartbeatTransaction: heartbeat, ClosingAmount: uint64Ptr(uint64(stxn.ClosingAmount)), ConfirmedRound: uint64Ptr(extra.Round), IntraRoundOffset: uint64Ptr(uint64(extra.Intra)), @@ -752,9 +747,14 @@ func edIndexToAddress(index uint64, txn sdk.Transaction, shared []sdk.Address) ( } func (si *ServerImplementation) assetParamsToAssetQuery(params generated.SearchForAssetsParams) (idb.AssetsQuery, error) { - creator, errorArr := decodeAddress(params.Creator, "creator", make([]string, 0)) - if len(errorArr) != 0 { - return idb.AssetsQuery{}, errors.New(errUnableToParseAddress) + + var creatorAddressBytes []byte + if params.Creator != nil { + creator, err := sdk.DecodeAddress(*params.Creator) + if err != nil { + return idb.AssetsQuery{}, fmt.Errorf("unable to parse creator address: %w", err) + } + creatorAddressBytes = creator[:] } var assetGreaterThan *uint64 @@ -769,7 +769,7 @@ func (si *ServerImplementation) assetParamsToAssetQuery(params generated.SearchF query := idb.AssetsQuery{ AssetID: params.AssetId, AssetIDGreaterThan: assetGreaterThan, - Creator: creator, + Creator: creatorAddressBytes, Name: strOrDefault(params.Name), Unit: strOrDefault(params.Unit), Query: "", @@ -781,9 +781,14 @@ func (si *ServerImplementation) assetParamsToAssetQuery(params generated.SearchF } func (si *ServerImplementation) appParamsToApplicationQuery(params generated.SearchForApplicationsParams) (idb.ApplicationQuery, error) { - addr, errorArr := decodeAddress(params.Creator, "creator", make([]string, 0)) - if len(errorArr) != 0 { - return idb.ApplicationQuery{}, errors.New(errUnableToParseAddress) + + var creatorAddressBytes []byte + if params.Creator != nil { + addr, err := sdk.DecodeAddress(*params.Creator) + if err != nil { + return idb.ApplicationQuery{}, fmt.Errorf("unable to parse creator address: %w", err) + } + creatorAddressBytes = addr[:] } var appGreaterThan *uint64 @@ -798,14 +803,83 @@ func (si *ServerImplementation) appParamsToApplicationQuery(params generated.Sea return idb.ApplicationQuery{ ApplicationID: params.ApplicationId, ApplicationIDGreaterThan: appGreaterThan, - Address: addr, + Address: creatorAddressBytes, IncludeDeleted: boolOrDefault(params.IncludeAll), Limit: min(uintOrDefaultValue(params.Limit, si.opts.DefaultApplicationsLimit), si.opts.MaxApplicationsLimit), }, nil } -func (si *ServerImplementation) blockParamsToBlockFilter(params generated.SearchForBlocksParams) (filter idb.BlockFilter, err error) { - var errorArr []string +func (si *ServerImplementation) transactionParamsToTransactionFilter(params generated.SearchForTransactionsParams) (filter idb.TransactionFilter, err error) { + var errorArr = make([]string, 0) + + // Integer + filter.MaxRound = uintOrDefault(params.MaxRound) + filter.MinRound = uintOrDefault(params.MinRound) + filter.AssetID = params.AssetId + filter.ApplicationID = params.ApplicationId + filter.Limit = min(uintOrDefaultValue(params.Limit, si.opts.DefaultTransactionsLimit), si.opts.MaxTransactionsLimit) + filter.Round = params.Round + + // String + filter.AddressRole, errorArr = decodeAddressRole((*string)(params.AddressRole), params.ExcludeCloseTo, errorArr) + filter.NextToken = strOrDefault(params.Next) + + // Address + if params.Address != nil { + addr, err := sdk.DecodeAddress(*params.Address) + if err != nil { + errorArr = append(errorArr, fmt.Sprintf("%s: %v", errUnableToParseAddress, err)) + } + filter.Address = addr[:] + } + + // Txid + filter.Txid, errorArr = decodeDigest(params.Txid, "txid", errorArr) + + // Byte array + filter.NotePrefix, errorArr = decodeBase64Byte(params.NotePrefix, "note-prefix", errorArr) + + // Group ID + filter.GroupID, errorArr = decodeGroupID(params.GroupId, "group-id", errorArr) + + // Time + if params.AfterTime != nil { + filter.AfterTime = *params.AfterTime + } + if params.BeforeTime != nil { + filter.BeforeTime = *params.BeforeTime + } + + // Enum + filter.SigType, errorArr = decodeSigType((*string)(params.SigType), errorArr) + filter.TypeEnum, errorArr = decodeType((*string)(params.TxType), errorArr) + + // Boolean + filter.RekeyTo = params.RekeyTo + + // filter Algos or Asset but not both. + if filter.AssetID != nil || filter.TypeEnum == idb.TypeEnumAssetTransfer { + filter.AssetAmountLT = params.CurrencyLessThan + filter.AssetAmountGT = params.CurrencyGreaterThan + } else { + filter.AlgosLT = params.CurrencyLessThan + filter.AlgosGT = params.CurrencyGreaterThan + } + + // If there were any errorArr while setting up the TransactionFilter, return now. + if len(errorArr) > 0 { + err = errors.New("invalid input: " + strings.Join(errorArr, ", ")) + + // clear out the intermediates. + filter = idb.TransactionFilter{} + } + + return +} + +func (si *ServerImplementation) blockParamsToBlockFilter(params generated.SearchForBlockHeadersParams) (filter idb.BlockHeaderFilter, err error) { + + var errs []error // Integer filter.Limit = min(uintOrDefaultValue(params.Limit, si.opts.DefaultBlocksLimit), si.opts.MaxBlocksLimit) @@ -814,7 +888,7 @@ func (si *ServerImplementation) blockParamsToBlockFilter(params generated.Search // This check is performed here instead of in validateBlockFilter because // when converting params into a filter, the next token is merged with params.MinRound. if params.MinRound != nil && params.MaxRound != nil && *params.MinRound > *params.MaxRound { - errorArr = append(errorArr, errInvalidRoundMinMax) + errs = append(errs, errors.New(errInvalidRoundMinMax)) } filter.MaxRound = params.MaxRound filter.MinRound = params.MinRound @@ -823,7 +897,7 @@ func (si *ServerImplementation) blockParamsToBlockFilter(params generated.Search if params.Next != nil { n, err := idb.DecodeBlockRowNext(*params.Next) if err != nil { - errorArr = append(errorArr, fmt.Sprintf("%s: %v", errUnableToParseNext, err)) + errs = append(errs, fmt.Errorf("%s: %w", errUnableToParseNext, err)) } // Set the MinRound if filter.MinRound == nil { @@ -845,7 +919,7 @@ func (si *ServerImplementation) blockParamsToBlockFilter(params generated.Search { // Make sure at most one of the participation parameters is set numParticipationFilters := 0 - if params.Proposer != nil { + if params.Proposers != nil || params.Proposer != nil { numParticipationFilters++ } if params.Expired != nil { @@ -861,150 +935,109 @@ func (si *ServerImplementation) blockParamsToBlockFilter(params generated.Search numParticipationFilters++ } if numParticipationFilters > 1 { - errorArr = append(errorArr, "only one of `proposer`, `expired`, `absent`, `updates`, or `participation` can be specified") + errs = append(errs, errors.New("only one of `proposers`, `expired`, or `absent`, `updates`, or `participation` can be specified")) } // Validate the number of items in the participation account lists - if params.Proposer != nil && uint64(len(*params.Proposer)) > si.opts.MaxAccountListSize { - errorArr = append(errorArr, fmt.Sprintf("proposer list too long, max size is %d", si.opts.MaxAccountListSize)) + if params.Proposers != nil && uint64(len(*params.Proposers)) > si.opts.MaxAccountListSize { + errs = append(errs, fmt.Errorf("proposers list too long, max size is %d", si.opts.MaxAccountListSize)) } if params.Expired != nil && uint64(len(*params.Expired)) > si.opts.MaxAccountListSize { - errorArr = append(errorArr, fmt.Sprintf("expired list too long, max size is %d", si.opts.MaxAccountListSize)) + errs = append(errs, fmt.Errorf("expired list too long, max size is %d", si.opts.MaxAccountListSize)) } if params.Absent != nil && uint64(len(*params.Absent)) > si.opts.MaxAccountListSize { - errorArr = append(errorArr, fmt.Sprintf("absent list too long, max size is %d", si.opts.MaxAccountListSize)) + errs = append(errs, fmt.Errorf("absent list too long, max size is %d", si.opts.MaxAccountListSize)) } if params.Updates != nil && uint64(len(*params.Updates)) > si.opts.MaxAccountListSize { - errorArr = append(errorArr, fmt.Sprintf("updates list too long, max size is %d", si.opts.MaxAccountListSize)) + errs = append(errs, fmt.Errorf("updates list too long, max size is %d", si.opts.MaxAccountListSize)) } if params.Participation != nil && uint64(len(*params.Participation)) > si.opts.MaxAccountListSize { - errorArr = append(errorArr, fmt.Sprintf("participation list too long, max size is %d", si.opts.MaxAccountListSize)) + errs = append(errs, fmt.Errorf("participation list too long, max size is %d", si.opts.MaxAccountListSize)) } + // There are two parameter aliases to filter by proposers: `proposer` and `proposers` filter.Proposers = make(map[sdk.Address]struct{}, 0) + if params.Proposers != nil { + for _, s := range *params.Proposers { + addr, err := sdk.DecodeAddress(s) + if err != nil { + errs = append(errs, fmt.Errorf("unable to parse proposers address `%s`: %w", s, err)) + } else { + filter.Proposers[addr] = struct{}{} + } + } + } if params.Proposer != nil { for _, s := range *params.Proposer { - var addr sdk.Address - addr, errorArr = decodeSdkAddress(s, "proposer", errorArr) - filter.Proposers[addr] = struct{}{} + addr, err := sdk.DecodeAddress(s) + if err != nil { + errs = append(errs, fmt.Errorf("unable to parse proposer address `%s`: %w", s, err)) + } else { + filter.Proposers[addr] = struct{}{} + } } } filter.ExpiredParticipationAccounts = make(map[sdk.Address]struct{}, 0) if params.Expired != nil { for _, s := range *params.Expired { - var addr sdk.Address - addr, errorArr = decodeSdkAddress(s, "expired", errorArr) - filter.ExpiredParticipationAccounts[addr] = struct{}{} + addr, err := sdk.DecodeAddress(s) + if err != nil { + errs = append(errs, fmt.Errorf("unable to parse expired address `%s`: %w", s, err)) + } else { + filter.ExpiredParticipationAccounts[addr] = struct{}{} + } } } filter.AbsentParticipationAccounts = make(map[sdk.Address]struct{}, 0) if params.Absent != nil { for _, s := range *params.Absent { - var addr sdk.Address - addr, errorArr = decodeSdkAddress(s, "absent", errorArr) - filter.AbsentParticipationAccounts[addr] = struct{}{} + addr, err := sdk.DecodeAddress(s) + if err != nil { + errs = append(errs, fmt.Errorf("unable to parse absent address `%s`: %w", s, err)) + } else { + filter.AbsentParticipationAccounts[addr] = struct{}{} + } } } // Updates = absent || expired if params.Updates != nil { for _, s := range *params.Updates { - var addr sdk.Address - addr, errorArr = decodeSdkAddress(s, "updates", errorArr) - filter.AbsentParticipationAccounts[addr] = struct{}{} - filter.ExpiredParticipationAccounts[addr] = struct{}{} + addr, err := sdk.DecodeAddress(s) + if err != nil { + errs = append(errs, fmt.Errorf("unable to parse updates address `%s`: %w", s, err)) + } else { + filter.AbsentParticipationAccounts[addr] = struct{}{} + filter.ExpiredParticipationAccounts[addr] = struct{}{} + } } } - // Participation = proposer || absent || expired + // Participation = proposers || absent || expired if params.Participation != nil { for _, s := range *params.Participation { - var addr sdk.Address - addr, errorArr = decodeSdkAddress(s, "participation", errorArr) - filter.Proposers[addr] = struct{}{} - filter.AbsentParticipationAccounts[addr] = struct{}{} - filter.ExpiredParticipationAccounts[addr] = struct{}{} + addr, err := sdk.DecodeAddress(s) + if err != nil { + errs = append(errs, fmt.Errorf("unable to parse participation address `%s`: %w", s, err)) + } else { + filter.Proposers[addr] = struct{}{} + filter.AbsentParticipationAccounts[addr] = struct{}{} + filter.ExpiredParticipationAccounts[addr] = struct{}{} + } } } } - // If there were any errorArr while setting up the BlockFilter, return now. - if len(errorArr) > 0 { - err = errors.New("invalid input: " + strings.Join(errorArr, ", ")) - - // clear out the intermediates. - filter = idb.BlockFilter{} - } - return -} - -func (si *ServerImplementation) transactionParamsToTransactionFilter(params generated.SearchForTransactionsParams) (filter idb.TransactionFilter, err error) { - var errorArr = make([]string, 0) - - // Integer - filter.MaxRound = uintOrDefault(params.MaxRound) - filter.MinRound = uintOrDefault(params.MinRound) - filter.AssetID = params.AssetId - filter.ApplicationID = params.ApplicationId - filter.Limit = min(uintOrDefaultValue(params.Limit, si.opts.DefaultTransactionsLimit), si.opts.MaxTransactionsLimit) - filter.Round = params.Round - - // String - filter.AddressRole, errorArr = decodeAddressRole((*string)(params.AddressRole), params.ExcludeCloseTo, errorArr) - filter.NextToken = strOrDefault(params.Next) - - // Address - filter.Address, errorArr = decodeAddress(params.Address, "address", errorArr) - filter.Txid, errorArr = decodeDigest(params.Txid, "txid", errorArr) - - // Byte array - filter.NotePrefix, errorArr = decodeBase64Byte(params.NotePrefix, "note-prefix", errorArr) - - // Group ID - filter.GroupID, errorArr = decodeGroupID(params.GroupId, "group-id", errorArr) - - // Time - if params.AfterTime != nil { - filter.AfterTime = *params.AfterTime - } - if params.BeforeTime != nil { - filter.BeforeTime = *params.BeforeTime - } - - // Enum - filter.SigType, errorArr = decodeSigType((*string)(params.SigType), errorArr) - filter.TypeEnum, errorArr = decodeType((*string)(params.TxType), errorArr) - - // Boolean - filter.RekeyTo = params.RekeyTo - - // filter Algos or Asset but not both. - if filter.AssetID != nil || filter.TypeEnum == idb.TypeEnumAssetTransfer { - filter.AssetAmountLT = params.CurrencyLessThan - filter.AssetAmountGT = params.CurrencyGreaterThan - } else { - filter.AlgosLT = params.CurrencyLessThan - filter.AlgosGT = params.CurrencyGreaterThan - } - - // If there were any errorArr while setting up the TransactionFilter, return now. - if len(errorArr) > 0 { - err = errors.New("invalid input: " + strings.Join(errorArr, ", ")) - - // clear out the intermediates. - filter = idb.TransactionFilter{} - } - - return + return filter, errors.Join(errs...) } func (si *ServerImplementation) maxAccountsErrorToAccountsErrorResponse(maxErr idb.MaxAPIResourcesPerAccountError) generated.ErrorResponse { addr := maxErr.Address.String() - max := uint64(si.opts.MaxAPIResourcesPerAccount) + maxResults := si.opts.MaxAPIResourcesPerAccount extraData := map[string]interface{}{ - "max-results": max, + "max-results": maxResults, "address": addr, "total-assets-opted-in": maxErr.TotalAssets, "total-created-assets": maxErr.TotalAssetParams, diff --git a/api/error_messages.go b/api/error_messages.go index 0bc4fb348..0060e5214 100644 --- a/api/error_messages.go +++ b/api/error_messages.go @@ -38,7 +38,8 @@ const ( errMultipleApplications = "multiple applications found for this id, please contact us, this shouldn't happen" ErrMultipleBoxes = "multiple application boxes found for this app id and box name, please contact us, this shouldn't happen" ErrFailedLookingUpBoxes = "failed while looking up application boxes" - errRewindingAccountNotSupported = "rewinding account is no longer supported, please remove the `round=` query parameter and try again" + errMultiAcctRewind = "multiple accounts rewind is not supported by this server" + errRewindingAccount = "error while rewinding account" errLookingUpBlockForRound = "error while looking up block for round" errBlockHeaderSearch = "error while searching for block headers" errTransactionSearch = "error while searching for transaction" diff --git a/api/generate.sh b/api/generate.sh deleted file mode 100755 index 86dae2a06..000000000 --- a/api/generate.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -set -e - -rootdir=`dirname $0` -pushd $rootdir - -# Convert v2 to v3 -curl -s -X POST "https://converter.swagger.io/api/convert" -H "accept: application/json" -H "Content-Type: application/json" -d @./indexer.oas2.json -o 3.json - -python3 -c "import json; import sys; json.dump(json.load(sys.stdin), sys.stdout, indent=2, sort_keys=True)" < 3.json > indexer.oas3.yml -rm 3.json - -echo "generating code." -oapi-codegen -package generated -type-mappings integer=uint64 -generate types -o generated/v2/types.go -exclude-tags=common indexer.oas3.yml -oapi-codegen -package generated -type-mappings integer=uint64 -generate server,spec -o generated/v2/routes.go -exclude-tags=common indexer.oas3.yml - -oapi-codegen -package common -type-mappings integer=uint64 -generate types -o generated/common/types.go -include-tags=common indexer.oas3.yml -oapi-codegen -package common -type-mappings integer=uint64 -generate server,spec -o generated/common/routes.go -include-tags=common indexer.oas3.yml diff --git a/api/generated/common/routes.go b/api/generated/common/routes.go index a45fd6f6c..96cc47fff 100644 --- a/api/generated/common/routes.go +++ b/api/generated/common/routes.go @@ -72,194 +72,202 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "y5QWKyEdlW/5agU66/Gh/xcAAP//SKFC4MsCAQA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/generated/common/types.go b/api/generated/common/types.go index e9f94eea7..cc5008f60 100644 --- a/api/generated/common/types.go +++ b/api/generated/common/types.go @@ -36,6 +36,7 @@ const ( TransactionTxTypeAfrz TransactionTxType = "afrz" TransactionTxTypeAppl TransactionTxType = "appl" TransactionTxTypeAxfer TransactionTxType = "axfer" + TransactionTxTypeHb TransactionTxType = "hb" TransactionTxTypeKeyreg TransactionTxType = "keyreg" TransactionTxTypePay TransactionTxType = "pay" TransactionTxTypeStpf TransactionTxType = "stpf" @@ -61,6 +62,7 @@ const ( TxTypeAfrz TxType = "afrz" TxTypeAppl TxType = "appl" TxTypeAxfer TxType = "axfer" + TxTypeHb TxType = "hb" TxTypeKeyreg TxType = "keyreg" TxTypePay TxType = "pay" TxTypeStpf TxType = "stpf" @@ -623,6 +625,24 @@ type HashFactory struct { // * sha256 type Hashtype string +// HbProofFields \[hbprf\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online. +type HbProofFields struct { + // HbPk \[p\] Public key of the heartbeat message. + HbPk *[]byte `json:"hb-pk,omitempty"` + + // HbPk1sig \[p1s\] Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the key PK2. + HbPk1sig *[]byte `json:"hb-pk1sig,omitempty"` + + // HbPk2 \[p2\] Key for new-style two-level ephemeral signature. + HbPk2 *[]byte `json:"hb-pk2,omitempty"` + + // HbPk2sig \[p2s\] Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master key (OneTimeSignatureVerifier). + HbPk2sig *[]byte `json:"hb-pk2sig,omitempty"` + + // HbSig \[s\] Signature of the heartbeat message. + HbSig *[]byte `json:"hb-sig,omitempty"` +} + // HealthCheck A health check response. type HealthCheck struct { Data *map[string]interface{} `json:"data,omitempty"` @@ -889,6 +909,12 @@ type Transaction struct { // Group \[grp\] Base64 encoded byte array of a sha512/256 digest. When present indicates that this transaction is part of a transaction group and the value is the sha512/256 hash of the transactions in that group. Group *[]byte `json:"group,omitempty"` + // HeartbeatTransaction Fields for a heartbeat transaction. + // + // Definition: + // data/transactions/heartbeat.go : HeartbeatTxnFields + HeartbeatTransaction *TransactionHeartbeat `json:"heartbeat-transaction,omitempty"` + // Id Transaction ID Id *string `json:"id,omitempty"` @@ -959,6 +985,7 @@ type Transaction struct { // * \[afrz\] asset-freeze-transaction // * \[appl\] application-transaction // * \[stpf\] state-proof-transaction + // * \[hb\] heartbeat-transaction TxType TransactionTxType `json:"tx-type"` } @@ -972,6 +999,7 @@ type Transaction struct { // * \[afrz\] asset-freeze-transaction // * \[appl\] application-transaction // * \[stpf\] state-proof-transaction +// * \[hb\] heartbeat-transaction type TransactionTxType string // TransactionApplication Fields for application transactions. @@ -1081,6 +1109,27 @@ type TransactionAssetTransfer struct { Sender *string `json:"sender,omitempty"` } +// TransactionHeartbeat Fields for a heartbeat transaction. +// +// Definition: +// data/transactions/heartbeat.go : HeartbeatTxnFields +type TransactionHeartbeat struct { + // HbAddress \[hbad\] HbAddress is the account this txn is proving onlineness for. + HbAddress string `json:"hb-address"` + + // HbKeyDilution \[hbkd\] HbKeyDilution must match HbAddress account's current KeyDilution. + HbKeyDilution uint64 `json:"hb-key-dilution"` + + // HbProof \[hbprf\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online. + HbProof HbProofFields `json:"hb-proof"` + + // HbSeed \[hbsd\] HbSeed must be the block seed for the this transaction's firstValid block. + HbSeed []byte `json:"hb-seed"` + + // HbVoteId \[hbvid\] HbVoteID must match the HbAddress account's current VoteID. + HbVoteId []byte `json:"hb-vote-id"` +} + // TransactionKeyreg Fields for a keyreg transaction. // // Definition: @@ -1281,6 +1330,9 @@ type Participation = []string // Proposer defines model for proposer. type Proposer = []string +// Proposers defines model for proposers. +type Proposers = []string + // RekeyTo defines model for rekey-to. type RekeyTo = bool diff --git a/api/generated/v2/routes.go b/api/generated/v2/routes.go index 93d1162dc..4d3984127 100644 --- a/api/generated/v2/routes.go +++ b/api/generated/v2/routes.go @@ -69,6 +69,9 @@ type ServerInterface interface { // (GET /v2/assets/{asset-id}/transactions) LookupAssetTransactions(ctx echo.Context, assetId uint64, params LookupAssetTransactionsParams) error + // (GET /v2/block-headers) + SearchForBlockHeaders(ctx echo.Context, params SearchForBlockHeadersParams) error + // (GET /v2/blocks) SearchForBlocks(ctx echo.Context, params SearchForBlocksParams) error @@ -977,6 +980,101 @@ func (w *ServerInterfaceWrapper) LookupAssetTransactions(ctx echo.Context) error return err } +// SearchForBlockHeaders converts echo context to params. +func (w *ServerInterfaceWrapper) SearchForBlockHeaders(ctx echo.Context) error { + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params SearchForBlockHeadersParams + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) + } + + // ------------- Optional query parameter "next" ------------- + + err = runtime.BindQueryParameter("form", true, false, "next", ctx.QueryParams(), ¶ms.Next) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter next: %s", err)) + } + + // ------------- Optional query parameter "min-round" ------------- + + err = runtime.BindQueryParameter("form", true, false, "min-round", ctx.QueryParams(), ¶ms.MinRound) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter min-round: %s", err)) + } + + // ------------- Optional query parameter "max-round" ------------- + + err = runtime.BindQueryParameter("form", true, false, "max-round", ctx.QueryParams(), ¶ms.MaxRound) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter max-round: %s", err)) + } + + // ------------- Optional query parameter "before-time" ------------- + + err = runtime.BindQueryParameter("form", true, false, "before-time", ctx.QueryParams(), ¶ms.BeforeTime) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter before-time: %s", err)) + } + + // ------------- Optional query parameter "after-time" ------------- + + err = runtime.BindQueryParameter("form", true, false, "after-time", ctx.QueryParams(), ¶ms.AfterTime) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter after-time: %s", err)) + } + + // ------------- Optional query parameter "proposer" ------------- + + err = runtime.BindQueryParameter("form", false, false, "proposer", ctx.QueryParams(), ¶ms.Proposer) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter proposer: %s", err)) + } + + // ------------- Optional query parameter "proposers" ------------- + + err = runtime.BindQueryParameter("form", false, false, "proposers", ctx.QueryParams(), ¶ms.Proposers) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter proposers: %s", err)) + } + + // ------------- Optional query parameter "expired" ------------- + + err = runtime.BindQueryParameter("form", false, false, "expired", ctx.QueryParams(), ¶ms.Expired) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter expired: %s", err)) + } + + // ------------- Optional query parameter "absent" ------------- + + err = runtime.BindQueryParameter("form", false, false, "absent", ctx.QueryParams(), ¶ms.Absent) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter absent: %s", err)) + } + + // ------------- Optional query parameter "updates" ------------- + + err = runtime.BindQueryParameter("form", false, false, "updates", ctx.QueryParams(), ¶ms.Updates) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter updates: %s", err)) + } + + // ------------- Optional query parameter "participation" ------------- + + err = runtime.BindQueryParameter("form", false, false, "participation", ctx.QueryParams(), ¶ms.Participation) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter participation: %s", err)) + } + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.SearchForBlockHeaders(ctx, params) + return err +} + // SearchForBlocks converts echo context to params. func (w *ServerInterfaceWrapper) SearchForBlocks(ctx echo.Context) error { var err error @@ -1032,6 +1130,13 @@ func (w *ServerInterfaceWrapper) SearchForBlocks(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter proposer: %s", err)) } + // ------------- Optional query parameter "proposers" ------------- + + err = runtime.BindQueryParameter("form", false, false, "proposers", ctx.QueryParams(), ¶ms.Proposers) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter proposers: %s", err)) + } + // ------------- Optional query parameter "expired" ------------- err = runtime.BindQueryParameter("form", false, false, "expired", ctx.QueryParams(), ¶ms.Expired) @@ -1301,6 +1406,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.GET(baseURL+"/v2/assets/:asset-id", wrapper.LookupAssetByID, m...) router.GET(baseURL+"/v2/assets/:asset-id/balances", wrapper.LookupAssetBalances, m...) router.GET(baseURL+"/v2/assets/:asset-id/transactions", wrapper.LookupAssetTransactions, m...) + router.GET(baseURL+"/v2/block-headers", wrapper.SearchForBlockHeaders, m...) router.GET(baseURL+"/v2/blocks", wrapper.SearchForBlocks, m...) router.GET(baseURL+"/v2/blocks/:round-number", wrapper.LookupBlock, m...) router.GET(baseURL+"/v2/transactions", wrapper.SearchForTransactions, m...) @@ -1311,239 +1417,250 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "jDA/mT2efPz/AQAA///vjMzzdaUBAA==", + "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", + "jDA/OXk8+fT/AgAA//+iEaM6j8ABAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/generated/v2/types.go b/api/generated/v2/types.go index e66af16fe..2b373c4c7 100644 --- a/api/generated/v2/types.go +++ b/api/generated/v2/types.go @@ -36,6 +36,7 @@ const ( TransactionTxTypeAfrz TransactionTxType = "afrz" TransactionTxTypeAppl TransactionTxType = "appl" TransactionTxTypeAxfer TransactionTxType = "axfer" + TransactionTxTypeHb TransactionTxType = "hb" TransactionTxTypeKeyreg TransactionTxType = "keyreg" TransactionTxTypePay TransactionTxType = "pay" TransactionTxTypeStpf TransactionTxType = "stpf" @@ -61,6 +62,7 @@ const ( TxTypeAfrz TxType = "afrz" TxTypeAppl TxType = "appl" TxTypeAxfer TxType = "axfer" + TxTypeHb TxType = "hb" TxTypeKeyreg TxType = "keyreg" TxTypePay TxType = "pay" TxTypeStpf TxType = "stpf" @@ -92,6 +94,7 @@ const ( LookupAccountTransactionsParamsTxTypeAfrz LookupAccountTransactionsParamsTxType = "afrz" LookupAccountTransactionsParamsTxTypeAppl LookupAccountTransactionsParamsTxType = "appl" LookupAccountTransactionsParamsTxTypeAxfer LookupAccountTransactionsParamsTxType = "axfer" + LookupAccountTransactionsParamsTxTypeHb LookupAccountTransactionsParamsTxType = "hb" LookupAccountTransactionsParamsTxTypeKeyreg LookupAccountTransactionsParamsTxType = "keyreg" LookupAccountTransactionsParamsTxTypePay LookupAccountTransactionsParamsTxType = "pay" LookupAccountTransactionsParamsTxTypeStpf LookupAccountTransactionsParamsTxType = "stpf" @@ -110,6 +113,7 @@ const ( LookupAssetTransactionsParamsTxTypeAfrz LookupAssetTransactionsParamsTxType = "afrz" LookupAssetTransactionsParamsTxTypeAppl LookupAssetTransactionsParamsTxType = "appl" LookupAssetTransactionsParamsTxTypeAxfer LookupAssetTransactionsParamsTxType = "axfer" + LookupAssetTransactionsParamsTxTypeHb LookupAssetTransactionsParamsTxType = "hb" LookupAssetTransactionsParamsTxTypeKeyreg LookupAssetTransactionsParamsTxType = "keyreg" LookupAssetTransactionsParamsTxTypePay LookupAssetTransactionsParamsTxType = "pay" LookupAssetTransactionsParamsTxTypeStpf LookupAssetTransactionsParamsTxType = "stpf" @@ -135,6 +139,7 @@ const ( Afrz SearchForTransactionsParamsTxType = "afrz" Appl SearchForTransactionsParamsTxType = "appl" Axfer SearchForTransactionsParamsTxType = "axfer" + Hb SearchForTransactionsParamsTxType = "hb" Keyreg SearchForTransactionsParamsTxType = "keyreg" Pay SearchForTransactionsParamsTxType = "pay" Stpf SearchForTransactionsParamsTxType = "stpf" @@ -711,6 +716,24 @@ type HashFactory struct { // * sha256 type Hashtype string +// HbProofFields \[hbprf\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online. +type HbProofFields struct { + // HbPk \[p\] Public key of the heartbeat message. + HbPk *[]byte `json:"hb-pk,omitempty"` + + // HbPk1sig \[p1s\] Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the key PK2. + HbPk1sig *[]byte `json:"hb-pk1sig,omitempty"` + + // HbPk2 \[p2\] Key for new-style two-level ephemeral signature. + HbPk2 *[]byte `json:"hb-pk2,omitempty"` + + // HbPk2sig \[p2s\] Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master key (OneTimeSignatureVerifier). + HbPk2sig *[]byte `json:"hb-pk2sig,omitempty"` + + // HbSig \[s\] Signature of the heartbeat message. + HbSig *[]byte `json:"hb-sig,omitempty"` +} + // HealthCheck A health check response. type HealthCheck struct { Data *map[string]interface{} `json:"data,omitempty"` @@ -977,6 +1000,12 @@ type Transaction struct { // Group \[grp\] Base64 encoded byte array of a sha512/256 digest. When present indicates that this transaction is part of a transaction group and the value is the sha512/256 hash of the transactions in that group. Group *[]byte `json:"group,omitempty"` + // HeartbeatTransaction Fields for a heartbeat transaction. + // + // Definition: + // data/transactions/heartbeat.go : HeartbeatTxnFields + HeartbeatTransaction *TransactionHeartbeat `json:"heartbeat-transaction,omitempty"` + // Id Transaction ID Id *string `json:"id,omitempty"` @@ -1047,6 +1076,7 @@ type Transaction struct { // * \[afrz\] asset-freeze-transaction // * \[appl\] application-transaction // * \[stpf\] state-proof-transaction + // * \[hb\] heartbeat-transaction TxType TransactionTxType `json:"tx-type"` } @@ -1060,6 +1090,7 @@ type Transaction struct { // * \[afrz\] asset-freeze-transaction // * \[appl\] application-transaction // * \[stpf\] state-proof-transaction +// * \[hb\] heartbeat-transaction type TransactionTxType string // TransactionApplication Fields for application transactions. @@ -1169,6 +1200,27 @@ type TransactionAssetTransfer struct { Sender *string `json:"sender,omitempty"` } +// TransactionHeartbeat Fields for a heartbeat transaction. +// +// Definition: +// data/transactions/heartbeat.go : HeartbeatTxnFields +type TransactionHeartbeat struct { + // HbAddress \[hbad\] HbAddress is the account this txn is proving onlineness for. + HbAddress string `json:"hb-address"` + + // HbKeyDilution \[hbkd\] HbKeyDilution must match HbAddress account's current KeyDilution. + HbKeyDilution uint64 `json:"hb-key-dilution"` + + // HbProof \[hbprf\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online. + HbProof HbProofFields `json:"hb-proof"` + + // HbSeed \[hbsd\] HbSeed must be the block seed for the this transaction's firstValid block. + HbSeed []byte `json:"hb-seed"` + + // HbVoteId \[hbvid\] HbVoteID must match the HbAddress account's current VoteID. + HbVoteId []byte `json:"hb-vote-id"` +} + // TransactionKeyreg Fields for a keyreg transaction. // // Definition: @@ -1369,6 +1421,9 @@ type Participation = []string // Proposer defines model for proposer. type Proposer = []string +// Proposers defines model for proposers. +type Proposers = []string + // RekeyTo defines model for rekey-to. type RekeyTo = bool @@ -1601,7 +1656,7 @@ type SearchForAccountsParams struct { // AuthAddr Include accounts configured to use this spending key. AuthAddr *string `form:"auth-addr,omitempty" json:"auth-addr,omitempty"` - // Round Deprecated and disallowed. This parameter used to include results for a specified round. Requests with this parameter set are now rejected. + // Round Include results for the specified round. For performance reasons, this parameter may be disabled on some configurations. Using application-id or asset-id filters will return both creator and opt-in accounts. Filtering by include-all will return creator and opt-in accounts for deleted assets and accounts. Non-opt-in managers are not included in the results when asset-id is used. Round *uint64 `form:"round,omitempty" json:"round,omitempty"` // ApplicationId Application ID @@ -1613,7 +1668,7 @@ type SearchForAccountsParamsExclude string // LookupAccountByIDParams defines parameters for LookupAccountByID. type LookupAccountByIDParams struct { - // Round Deprecated and disallowed. This parameter used to include results for a specified round. Requests with this parameter set are now rejected. + // Round Include results for the specified round. Round *uint64 `form:"round,omitempty" json:"round,omitempty"` // IncludeAll Include all items including closed accounts, deleted applications, destroyed assets, opted-out asset holdings, and closed-out application localstates. @@ -1913,6 +1968,45 @@ type LookupAssetTransactionsParamsSigType string // LookupAssetTransactionsParamsAddressRole defines parameters for LookupAssetTransactions. type LookupAssetTransactionsParamsAddressRole string +// SearchForBlockHeadersParams defines parameters for SearchForBlockHeaders. +type SearchForBlockHeadersParams struct { + // Limit Maximum number of results to return. There could be additional pages even if the limit is not reached. + Limit *uint64 `form:"limit,omitempty" json:"limit,omitempty"` + + // Next The next page of results. Use the next token provided by the previous results. + Next *string `form:"next,omitempty" json:"next,omitempty"` + + // MinRound Include results at or after the specified min-round. + MinRound *uint64 `form:"min-round,omitempty" json:"min-round,omitempty"` + + // MaxRound Include results at or before the specified max-round. + MaxRound *uint64 `form:"max-round,omitempty" json:"max-round,omitempty"` + + // BeforeTime Include results before the given time. Must be an RFC 3339 formatted string. + BeforeTime *time.Time `form:"before-time,omitempty" json:"before-time,omitempty"` + + // AfterTime Include results after the given time. Must be an RFC 3339 formatted string. + AfterTime *time.Time `form:"after-time,omitempty" json:"after-time,omitempty"` + + // Proposer This parameter is an alias to the `proposers` parameter, it exposes the exact same functionality. + Proposer *[]string `form:"proposer,omitempty" json:"proposer,omitempty"` + + // Proposers Accounts marked as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses. + Proposers *[]string `form:"proposers,omitempty" json:"proposers,omitempty"` + + // Expired Accounts marked as expired in the block header's participation updates. This parameter accepts a comma separated list of addresses. + Expired *[]string `form:"expired,omitempty" json:"expired,omitempty"` + + // Absent Accounts marked as absent in the block header's participation updates. This parameter accepts a comma separated list of addresses. + Absent *[]string `form:"absent,omitempty" json:"absent,omitempty"` + + // Updates Accounts marked as expired or absent in the block header's participation updates. This parameter accepts a comma separated list of addresses. + Updates *[]string `form:"updates,omitempty" json:"updates,omitempty"` + + // Participation Accounts marked as expired, absent or as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses. + Participation *[]string `form:"participation,omitempty" json:"participation,omitempty"` +} + // SearchForBlocksParams defines parameters for SearchForBlocks. type SearchForBlocksParams struct { // Limit Maximum number of results to return. There could be additional pages even if the limit is not reached. @@ -1933,19 +2027,22 @@ type SearchForBlocksParams struct { // AfterTime Include results after the given time. Must be an RFC 3339 formatted string. AfterTime *time.Time `form:"after-time,omitempty" json:"after-time,omitempty"` - // Proposer Account(s) marked as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses. + // Proposer This parameter is an alias to the `proposers` parameter, it exposes the exact same functionality. Proposer *[]string `form:"proposer,omitempty" json:"proposer,omitempty"` - // Expired Account(s) marked as expired in the block header's participation updates. This parameter accepts a comma separated list of addresses. + // Proposers Accounts marked as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses. + Proposers *[]string `form:"proposers,omitempty" json:"proposers,omitempty"` + + // Expired Accounts marked as expired in the block header's participation updates. This parameter accepts a comma separated list of addresses. Expired *[]string `form:"expired,omitempty" json:"expired,omitempty"` - // Absent Account(s) marked as absent in the block header's participation updates. This parameter accepts a comma separated list of addresses. + // Absent Accounts marked as absent in the block header's participation updates. This parameter accepts a comma separated list of addresses. Absent *[]string `form:"absent,omitempty" json:"absent,omitempty"` - // Updates Account(s) marked as expired or absent in the block header's participation updates. This parameter accepts a comma separated list of addresses. + // Updates Accounts marked as expired or absent in the block header's participation updates. This parameter accepts a comma separated list of addresses. Updates *[]string `form:"updates,omitempty" json:"updates,omitempty"` - // Participation Account(s) marked as expired, absent or as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses. + // Participation Accounts marked as expired, absent or as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses. Participation *[]string `form:"participation,omitempty" json:"participation,omitempty"` } diff --git a/api/handlers.go b/api/handlers.go index 6e3f48665..f18399121 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -17,6 +17,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/algorand/avm-abi/apps" + "github.com/algorand/indexer/v3/accounting" "github.com/algorand/indexer/v3/api/generated/common" "github.com/algorand/indexer/v3/api/generated/v2" "github.com/algorand/indexer/v3/idb" @@ -28,6 +29,14 @@ import ( // ServerImplementation implements the handler interface used by the generated route definitions. type ServerImplementation struct { + + // EnableAddressSearchRoundRewind is allows configuring whether or not the + // 'accounts' endpoint allows specifying a round number. This is done for + // performance reasons, because requesting many accounts at a particular + // round could put a lot of strain on the system (especially if the round + // is from long ago). + EnableAddressSearchRoundRewind bool + db idb.IndexerDb dataError func() error @@ -45,7 +54,7 @@ type ServerImplementation struct { // Helper functions // ////////////////////// -func validateBlockFilter(filter *idb.BlockFilter) error { +func validateBlockFilter(filter *idb.BlockHeaderFilter) error { var errorArr = make([]string, 0) // Int64 overflows @@ -209,14 +218,13 @@ func (si *ServerImplementation) LookupAccountByID(ctx echo.Context, accountID st if err := si.verifyHandler("LookupAccountByID", ctx); err != nil { return badRequest(ctx, err.Error()) } - // The Round parameter is no longer supported (as it was used to request account rewinding) - if params.Round != nil { - return badRequest(ctx, errRewindingAccountNotSupported) + if params.Round != nil && uint64(*params.Round) > math.MaxInt64 { + return notFound(ctx, errValueExceedingInt64) } - addr, decodeErrors := decodeAddress(&accountID, "account-id", make([]string, 0)) - if len(decodeErrors) != 0 { - return badRequest(ctx, decodeErrors[0]) + addr, err := sdk.DecodeAddress(accountID) + if err != nil { + return badRequest(ctx, fmt.Sprintf("%s: %v", errUnableToParseAddress, err)) } options := idb.AccountQueryOptions{ @@ -241,7 +249,7 @@ func (si *ServerImplementation) LookupAccountByID(ctx echo.Context, accountID st } } - accounts, round, err := si.fetchAccounts(ctx.Request().Context(), options) + accounts, round, err := si.fetchAccounts(ctx.Request().Context(), options, params.Round) if err != nil { var maxErr idb.MaxAPIResourcesPerAccountError if errors.As(err, &maxErr) { @@ -314,9 +322,9 @@ func (si *ServerImplementation) LookupAccountAssets(ctx echo.Context, accountID return notFound(ctx, errValueExceedingInt64) } - addr, errors := decodeAddress(&accountID, "account-id", make([]string, 0)) - if len(errors) != 0 { - return badRequest(ctx, errors[0]) + addr, err := sdk.DecodeAddress(accountID) + if err != nil { + return badRequest(ctx, fmt.Sprintf("%s: %v", errUnableToParseAddress, err)) } var assetGreaterThan *uint64 @@ -329,7 +337,7 @@ func (si *ServerImplementation) LookupAccountAssets(ctx echo.Context, accountID } query := idb.AssetBalanceQuery{ - Address: addr, + Address: addr[:], AssetID: params.AssetId, AssetIDGT: assetGreaterThan, IncludeDeleted: boolOrDefault(params.IncludeAll), @@ -399,18 +407,22 @@ func (si *ServerImplementation) SearchForAccounts(ctx echo.Context, params gener return badRequest(ctx, err.Error()) } if (params.AssetId != nil && uint64(*params.AssetId) > math.MaxInt64) || - (params.ApplicationId != nil && uint64(*params.ApplicationId) > math.MaxInt64) { + (params.ApplicationId != nil && uint64(*params.ApplicationId) > math.MaxInt64) || + (params.Round != nil && uint64(*params.Round) > math.MaxInt64) { return notFound(ctx, errValueExceedingInt64) } - // The Round parameter is no longer supported (as it was used to request account rewinding) - if params.Round != nil { - return badRequest(ctx, errRewindingAccountNotSupported) + if !si.EnableAddressSearchRoundRewind && params.Round != nil { + return badRequest(ctx, errMultiAcctRewind) } - spendingAddr, decodeErrors := decodeAddress(params.AuthAddr, "account-id", make([]string, 0)) - if len(decodeErrors) != 0 { - return badRequest(ctx, decodeErrors[0]) + var spendingAddrBytes []byte + if params.AuthAddr != nil { + spendingAddr, err := sdk.DecodeAddress(*params.AuthAddr) + if err != nil { + return badRequest(ctx, fmt.Sprintf("unable to parse auth addr: %v", err)) + } + spendingAddrBytes = spendingAddr[:] } options := idb.AccountQueryOptions{ @@ -421,7 +433,7 @@ func (si *ServerImplementation) SearchForAccounts(ctx echo.Context, params gener Limit: min(uintOrDefaultValue(params.Limit, si.opts.DefaultAccountsLimit), si.opts.MaxAccountsLimit), HasAssetID: uintOrDefault(params.AssetId), HasAppID: uintOrDefault(params.ApplicationId), - EqualToAuthAddr: spendingAddr[:], + EqualToAuthAddr: spendingAddrBytes, IncludeDeleted: boolOrDefault(params.IncludeAll), MaxResources: si.opts.MaxAPIResourcesPerAccount, } @@ -454,7 +466,7 @@ func (si *ServerImplementation) SearchForAccounts(ctx echo.Context, params gener options.GreaterThanAddress = addr[:] } - accounts, round, err := si.fetchAccounts(ctx.Request().Context(), options) + accounts, round, err := si.fetchAccounts(ctx.Request().Context(), options, params.Round) if err != nil { var maxErr idb.MaxAPIResourcesPerAccountError if errors.As(err, &maxErr) { @@ -486,10 +498,11 @@ func (si *ServerImplementation) LookupAccountTransactions(ctx echo.Context, acco if (params.AssetId != nil && uint64(*params.AssetId) > math.MaxInt64) || (params.Round != nil && uint64(*params.Round) > math.MaxInt64) { return notFound(ctx, errValueExceedingInt64) } + // Check that a valid account was provided - _, errors := decodeAddress(strPtr(accountID), "account-id", make([]string, 0)) - if len(errors) != 0 { - return badRequest(ctx, errors[0]) + _, err := sdk.DecodeAddress(accountID) + if err != nil { + return badRequest(ctx, fmt.Sprintf("%s: %v", errUnableToParseAddress, err)) } searchParams := generated.SearchForTransactionsParams{ @@ -621,7 +634,7 @@ func (si *ServerImplementation) LookupApplicationBoxByIDAndName(ctx echo.Context if len(boxes) == 0 { // this is an unexpected situation as should have received a sql.ErrNoRows from fetchApplicationBoxes's err msg := fmt.Sprintf("%s: round=?=%d, appid=%d, boxName=%s", ErrFailedLookingUpBoxes, round, applicationID, encodedBoxName) - return indexerError(ctx, fmt.Errorf(msg)) + return indexerError(ctx, errors.New(msg)) } if appid != generated.ApplicationId(applicationID) { @@ -689,7 +702,7 @@ func (si *ServerImplementation) SearchForApplicationBoxes(ctx echo.Context, appl if len(boxes) == 0 { // this is an unexpected situation as should have received a sql.ErrNoRows from fetchApplicationBoxes's err msg := fmt.Sprintf("%s: round=?=%d, appid=%d", errFailedSearchingBoxes, round, applicationID) - return indexerError(ctx, fmt.Errorf(msg)) + return indexerError(ctx, errors.New(msg)) } if appid != generated.ApplicationId(applicationID) { @@ -1002,11 +1015,17 @@ func (si *ServerImplementation) LookupTransaction(ctx echo.Context, txid string) return ctx.JSON(http.StatusOK, response) } -// SearchForBlocks returns block headers matching the provided parameters +// SearchForBlocks is an alias for SearchForBlockHeaders // (GET /v2/blocks) func (si *ServerImplementation) SearchForBlocks(ctx echo.Context, params generated.SearchForBlocksParams) error { + return si.SearchForBlockHeaders(ctx, (generated.SearchForBlockHeadersParams)(params)) +} + +// SearchForBlockHeaders returns block headers matching the provided parameters +// (GET /v2/blocks) +func (si *ServerImplementation) SearchForBlockHeaders(ctx echo.Context, params generated.SearchForBlockHeadersParams) error { // Validate query parameters - if err := si.verifyHandler("SearchForBlocks", ctx); err != nil { + if err := si.verifyHandler("SearchForBlockHeaders", ctx); err != nil { return badRequest(ctx, err.Error()) } @@ -1027,7 +1046,7 @@ func (si *ServerImplementation) SearchForBlocks(ctx echo.Context, params generat } // Populate the response model and render it - response := generated.BlocksResponse{ + response := generated.BlockHeadersResponse{ CurrentRound: round, NextToken: strPtr(next), Blocks: blockHeaders, @@ -1035,6 +1054,46 @@ func (si *ServerImplementation) SearchForBlocks(ctx echo.Context, params generat return ctx.JSON(http.StatusOK, response) } +// fetchBlockHeaders is used to query the backend for block headers, and compute the next token +func (si *ServerImplementation) fetchBlockHeaders(ctx context.Context, bf idb.BlockHeaderFilter) ([]generated.BlockHeader, string, uint64 /*round*/, error) { + + var round uint64 + var nextToken string + results := make([]generated.BlockHeader, 0) + err := callWithTimeout(ctx, si.log, si.timeout, func(ctx context.Context) error { + + // Open a channel from which result rows will be received + var rows <-chan idb.BlockRow + rows, round = si.db.BlockHeaders(ctx, bf) + + // Iterate received rows, converting each to a generated.Block + var lastRow idb.BlockRow + for row := range rows { + if row.Error != nil { + return row.Error + } + + results = append(results, hdrRowToBlock(row)) + lastRow = row + } + + // No next token if there were no results. + if len(results) == 0 { + return nil + } + + // Generate the next token and return + var err error + nextToken, err = lastRow.Next() + return err + }) + if err != nil { + return nil, "", 0, err + } + + return results, nextToken, round, nil +} + // SearchForTransactions returns transactions matching the provided parameters // (GET /v2/transactions) func (si *ServerImplementation) SearchForTransactions(ctx echo.Context, params generated.SearchForTransactionsParams) error { @@ -1438,7 +1497,7 @@ func (si *ServerImplementation) fetchBlock(ctx context.Context, round uint64, op // fetchAccounts queries for accounts and converts them into generated.Account // objects, optionally rewinding their value back to a particular round. -func (si *ServerImplementation) fetchAccounts(ctx context.Context, options idb.AccountQueryOptions) ([]generated.Account, uint64 /*round*/, error) { +func (si *ServerImplementation) fetchAccounts(ctx context.Context, options idb.AccountQueryOptions, atRound *uint64) ([]generated.Account, uint64 /*round*/, error) { var round uint64 accounts := make([]generated.Account, 0) err := callWithTimeout(ctx, si.log, si.timeout, func(ctx context.Context) error { @@ -1451,12 +1510,33 @@ func (si *ServerImplementation) fetchAccounts(ctx context.Context, options idb.A } }() + if (atRound != nil) && (*atRound > round) { + return fmt.Errorf("%s: the requested round %d > the current round %d", + errRewindingAccount, *atRound, round) + } + for row := range accountchan { if row.Error != nil { return row.Error } - account := row.Account + // Compute for a given round if requested. + var account generated.Account + if atRound != nil { + acct, err := accounting.AccountAtRound(ctx, row.Account, *atRound, si.db) + if err != nil { + // Ignore the error if this is an account search rewind error + _, isSpecialAccountRewindError := err.(*accounting.SpecialAccountRewindError) + if len(options.EqualToAddress) != 0 || !isSpecialAccountRewindError { + return fmt.Errorf("%s: %v", errRewindingAccount, err) + } + // If we didn't return, continue to the next account + continue + } + account = acct + } else { + account = row.Account + } // match the algod equivalent which includes pending rewards account.Rewards += account.PendingRewards @@ -1470,46 +1550,6 @@ func (si *ServerImplementation) fetchAccounts(ctx context.Context, options idb.A return accounts, round, nil } -// fetchBlockHeaders is used to query the backend for block headers, and compute the next token -func (si *ServerImplementation) fetchBlockHeaders(ctx context.Context, bf idb.BlockFilter) ([]generated.Block, string, uint64 /*round*/, error) { - - var round uint64 - var nextToken string - results := make([]generated.Block, 0) - err := callWithTimeout(ctx, si.log, si.timeout, func(ctx context.Context) error { - - // Open a channel from which result rows will be received - var rows <-chan idb.BlockRow - rows, round = si.db.Blocks(ctx, bf) - - // Iterate receieved rows, converting each to a generated.Block - var lastRow idb.BlockRow - for row := range rows { - if row.Error != nil { - return row.Error - } - - results = append(results, rowToBlock(&row.BlockHeader)) - lastRow = row - } - - // No next token if there were no results. - if len(results) == 0 { - return nil - } - - // Generate the next token and return - var err error - nextToken, err = lastRow.Next() - return err - }) - if err != nil { - return nil, "", 0, err - } - - return results, nextToken, round, nil -} - // fetchTransactions is used to query the backend for transactions, and compute the next token // If returnInnerTxnOnly is false, then the root txn is returned for a inner txn match. func (si *ServerImplementation) fetchTransactions(ctx context.Context, filter idb.TransactionFilter) ([]generated.Transaction, string, uint64 /*round*/, error) { diff --git a/api/handlers_e2e_test.go b/api/handlers_e2e_test.go index 0808b60cd..95a09ee58 100644 --- a/api/handlers_e2e_test.go +++ b/api/handlers_e2e_test.go @@ -19,6 +19,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/algorand/avm-abi/apps" "github.com/algorand/indexer/v3/api/generated/v2" "github.com/algorand/indexer/v3/idb" "github.com/algorand/indexer/v3/idb/postgres" @@ -27,7 +28,6 @@ import ( "github.com/algorand/indexer/v3/util" "github.com/algorand/indexer/v3/util/test" - "github.com/algorand/avm-abi/apps" "github.com/algorand/go-algorand-sdk/v2/crypto" "github.com/algorand/go-algorand-sdk/v2/encoding/json" sdk "github.com/algorand/go-algorand-sdk/v2/types" @@ -80,7 +80,7 @@ func setupIdb(t *testing.T, genesis sdk.Genesis) (*postgres.IndexerDb, func()) { Block: test.MakeGenesisBlock(), Delta: sdk.LedgerStateDelta{}, } - db.AddBlock(&vb) + require.NoError(t, db.AddBlock(&vb)) require.NoError(t, err) return db, newShutdownFunc @@ -1531,7 +1531,7 @@ func TestFetchBlockWithOptions(t *testing.T) { } for _, tc := range testCases { - t.Run(fmt.Sprintf(tc.name), func(t *testing.T) { + t.Run(tc.name, func(t *testing.T) { c, api, rec := setupReq("/v2/blocks/:round-number", "round-number", "1") if tc.name == "default" { err = api.LookupBlock(c, 1, generated.LookupBlockParams{}) @@ -1738,6 +1738,7 @@ func TestGetBlockWithCompression(t *testing.T) { // we now make sure that compression flag works with other flags. notCompressedBlock = getBlockFunc(t, true, false) compressedBlock = getBlockFunc(t, true, true) + require.Equal(t, notCompressedBlock, compressedBlock) require.Equal(t, len(*notCompressedBlock.Transactions), 0) } @@ -2613,3 +2614,39 @@ func TestAccounts(t *testing.T) { assert.Equal(t, uint64(1000000000000), (*acctA.Account.CreatedAssets)[0].Params.Total) assert.Equal(t, "bogo", *(*acctA.Account.CreatedAssets)[0].Params.UnitName) } + +func TestPNAHeader(t *testing.T) { + db, shutdownFunc := setupIdb(t, test.MakeGenesis()) + defer shutdownFunc() + + ////////// + // When // We preflight an endpoint with the PNA "Request" header set + ////////// + + serverCtx, serverCancel := context.WithCancel(context.Background()) + defer serverCancel() + opts := defaultOpts + opts.EnablePrivateNetworkAccessHeader = true + listenAddr := "localhost:8894" + go Serve(serverCtx, listenAddr, db, nil, logrus.New(), opts) + + waitForServer(t, listenAddr) + + path := "/health" + client := &http.Client{} + req, err := http.NewRequest("OPTIONS", "http://"+listenAddr+path, nil) + require.NoError(t, err) + req.Header.Add("Access-Control-Request-Private-Network", "true") + + t.Log("making HTTP request path", req.URL) + + resp, err := client.Do(req) + require.NoError(t, err) + + ////////// + // Then // We expect the PNA "Allow" header to be set + ////////// + + require.Equal(t, resp.Header.Get("Access-Control-Allow-Private-Network"), "true") + defer resp.Body.Close() +} diff --git a/api/handlers_test.go b/api/handlers_test.go index c5b2e40da..6f4134608 100644 --- a/api/handlers_test.go +++ b/api/handlers_test.go @@ -17,13 +17,14 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - sdkcrypto "github.com/algorand/go-algorand-sdk/v2/crypto" - "github.com/algorand/go-algorand-sdk/v2/encoding/msgpack" - sdk "github.com/algorand/go-algorand-sdk/v2/types" "github.com/algorand/indexer/v3/api/generated/v2" "github.com/algorand/indexer/v3/idb" "github.com/algorand/indexer/v3/idb/mocks" "github.com/algorand/indexer/v3/types" + + sdkcrypto "github.com/algorand/go-algorand-sdk/v2/crypto" + "github.com/algorand/go-algorand-sdk/v2/encoding/msgpack" + sdk "github.com/algorand/go-algorand-sdk/v2/types" ) func TestTransactionParamToTransactionFilter(t *testing.T) { @@ -642,6 +643,15 @@ func TestFetchTransactions(t *testing.T) { loadTransactionFromFile("test_resources/state_proof_with_index.response"), }, }, + { + name: "Heartbeat Txn", + txnBytes: [][]byte{ + loadResourceFileOrPanic("test_resources/heartbeat.txn"), + }, + response: []generated.Transaction{ + loadTransactionFromFile("test_resources/heartbeat.response"), + }, + }, } // use for the branch below and createTxn helper func to add a new test case @@ -654,8 +664,8 @@ func TestFetchTransactions(t *testing.T) { response []generated.Transaction created uint64 }{ - name: "State Proof Txn", - txnBytes: [][]byte{loadResourceFileOrPanic("test_resources/state_proof.txn")}, + name: "HeartBeat Txn", + txnBytes: [][]byte{loadResourceFileOrPanic("test_resources/heartbeat.txn")}, }) } for _, test := range tests { @@ -845,7 +855,7 @@ func TestTimeouts(t *testing.T) { errString: errTransactionSearch, mockCall: transactionFunc, callHandler: func(ctx echo.Context, si ServerImplementation) error { - return si.LookupAccountTransactions(ctx, "", generated.LookupAccountTransactionsParams{}) + return si.LookupAccountTransactions(ctx, "MONEYMBRSMUAM2NGL6PCEQEDVHFWAQB6DU47NUS6P5DJM4OJFN7E7DSVBA", generated.LookupAccountTransactionsParams{}) }, }, { @@ -991,12 +1001,13 @@ func TestApplicationLimits(t *testing.T) { }, } - // Mock backend to capture default limits - mockIndexer := &mocks.IndexerDb{} - si := testServerImplementation(mockIndexer) - si.timeout = 5 * time.Millisecond - for _, tc := range testcases { + + // Mock backend to capture default limits + mockIndexer := &mocks.IndexerDb{} + si := testServerImplementation(mockIndexer) + si.timeout = 5 * time.Millisecond + t.Run(tc.name, func(t *testing.T) { // Setup context... e := echo.New() @@ -1187,7 +1198,7 @@ func TestBigNumbers(t *testing.T) { c := e.NewContext(req, rec1) // call handler - tc.callHandler(c, *si) + require.NoError(t, tc.callHandler(c, *si)) assert.Equal(t, http.StatusNotFound, rec1.Code) bodyStr := rec1.Body.String() require.Contains(t, bodyStr, tc.errString) @@ -1195,53 +1206,6 @@ func TestBigNumbers(t *testing.T) { } } -func TestRewindRoundParameterRejected(t *testing.T) { - testcases := []struct { - name string - errString string - callHandler func(ctx echo.Context, si ServerImplementation) error - }{ - { - name: "SearchForAccountInvalidRound", - errString: errRewindingAccountNotSupported, - callHandler: func(ctx echo.Context, si ServerImplementation) error { - return si.SearchForAccounts(ctx, generated.SearchForAccountsParams{Round: uint64Ptr(uint64(math.MaxInt64 + 1))}) - }, - }, - { - name: "LookupAccountByID", - errString: errRewindingAccountNotSupported, - callHandler: func(ctx echo.Context, si ServerImplementation) error { - return si.LookupAccountByID(ctx, - "PBH2JQNVP5SBXLTOWNHHPGU6FUMBVS4ZDITPK5RA5FG2YIIFS6UYEMFM2Y", - generated.LookupAccountByIDParams{Round: uint64Ptr(uint64(math.MaxInt64 + 1))}) - }, - }, - } - - for _, tc := range testcases { - t.Run(tc.name, func(t *testing.T) { - - // Make a mock indexer. - mockIndexer := &mocks.IndexerDb{} - - si := testServerImplementation(mockIndexer) - - // Setup context... - e := echo.New() - req := httptest.NewRequest(http.MethodGet, "/", nil) - rec1 := httptest.NewRecorder() - c := e.NewContext(req, rec1) - - // call handler - tc.callHandler(c, *si) - assert.Equal(t, http.StatusBadRequest, rec1.Code) - bodyStr := rec1.Body.String() - require.Contains(t, bodyStr, tc.errString) - }) - } -} - func TestFetchBlock(t *testing.T) { testcases := []struct { name string diff --git a/api/indexer.oas2.json b/api/indexer.oas2.json index 83e786d37..fa84254fd 100644 --- a/api/indexer.oas2.json +++ b/api/indexer.oas2.json @@ -80,7 +80,7 @@ }, { "type": "integer", - "description": "Deprecated and disallowed. This parameter used to include results for a specified round. Requests with this parameter set are now rejected.", + "description": "Include results for the specified round. For performance reasons, this parameter may be disabled on some configurations. Using application-id or asset-id filters will return both creator and opt-in accounts. Filtering by include-all will return creator and opt-in accounts for deleted assets and accounts. Non-opt-in managers are not included in the results when asset-id is used.", "name": "round", "in": "query" }, @@ -119,10 +119,7 @@ "$ref": "#/parameters/account-id" }, { - "type": "integer", - "description": "Deprecated and disallowed. This parameter used to include results for a specified round. Requests with this parameter set are now rejected.", - "name": "round", - "in": "query" + "$ref": "#/parameters/round" }, { "$ref": "#/parameters/include-all" @@ -855,7 +852,7 @@ }, "/v2/blocks": { "get": { - "description": "Search for blocks. Blocks are returned in ascending round order. Transactions are not included in the output.", + "description": "This endpoint is an alias for GET /v2/blocks. The behavior is identical.", "consumes": [ "application/json" ], @@ -888,6 +885,73 @@ { "$ref": "#/parameters/proposer" }, + { + "$ref": "#/parameters/proposers" + }, + { + "$ref": "#/parameters/expired" + }, + { + "$ref": "#/parameters/absent" + }, + { + "$ref": "#/parameters/updates" + }, + { + "$ref": "#/parameters/participation" + } + ], + "responses": { + "200": { + "$ref": "#/responses/BlockHeadersResponse" + }, + "404": { + "$ref": "#/responses/ErrorResponse" + }, + "500": { + "$ref": "#/responses/ErrorResponse" + } + } + } + }, + "/v2/block-headers": { + "get": { + "description": "Search for block headers. Block headers are returned in ascending round order. Transactions are not included in the output.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "operationId": "searchForBlockHeaders", + "parameters": [ + { + "$ref": "#/parameters/limit" + }, + { + "$ref": "#/parameters/next" + }, + { + "$ref": "#/parameters/min-round" + }, + { + "$ref": "#/parameters/max-round" + }, + { + "$ref": "#/parameters/before-time" + }, + { + "$ref": "#/parameters/after-time" + }, + { + "$ref": "#/parameters/proposer" + }, + { + "$ref": "#/parameters/proposers" + }, { "$ref": "#/parameters/expired" }, @@ -2150,7 +2214,8 @@ "asset-transfer-transaction", "keyreg-transaction", "payment-transaction", - "state-proof-transaction" + "state-proof-transaction", + "heartbeat-transaction" ], "properties": { "application-transaction": { @@ -2168,6 +2233,9 @@ "state-proof-transaction": { "$ref": "#/definitions/TransactionStateProof" }, + "heartbeat-transaction": { + "$ref": "#/definitions/TransactionHeartbeat" + }, "auth-addr": { "description": "\\[sgnr\\] this is included with signed transactions when the signing address does not equal the sender. The backend can use this to ensure that auth addr is equal to the accounts auth addr.", "type": "string", @@ -2268,7 +2336,7 @@ "$ref": "#/definitions/TransactionSignature" }, "tx-type": { - "description": "\\[type\\] Indicates what type of transaction this is. Different types have different fields.\n\nValid types, and where their fields are stored:\n* \\[pay\\] payment-transaction\n* \\[keyreg\\] keyreg-transaction\n* \\[acfg\\] asset-config-transaction\n* \\[axfer\\] asset-transfer-transaction\n* \\[afrz\\] asset-freeze-transaction\n* \\[appl\\] application-transaction\n* \\[stpf\\] state-proof-transaction", + "description": "\\[type\\] Indicates what type of transaction this is. Different types have different fields.\n\nValid types, and where their fields are stored:\n* \\[pay\\] payment-transaction\n* \\[keyreg\\] keyreg-transaction\n* \\[acfg\\] asset-config-transaction\n* \\[axfer\\] asset-transfer-transaction\n* \\[afrz\\] asset-freeze-transaction\n* \\[appl\\] application-transaction\n* \\[stpf\\] state-proof-transaction\n* \\[hb\\] heartbeat-transaction", "type": "string", "enum": [ "pay", @@ -2277,7 +2345,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "x-algorand-format": "tx-type-enum" }, @@ -2435,6 +2504,41 @@ } } }, + "TransactionHeartbeat": { + "description": "Fields for a heartbeat transaction.\n\nDefinition:\ndata/transactions/heartbeat.go : HeartbeatTxnFields", + "type": "object", + "required": [ + "hb-address", + "hb-proof", + "hb-seed", + "hb-vote-id", + "hb-key-dilution" + ], + "properties": { + "hb-address": { + "description": "\\[hbad\\] HbAddress is the account this txn is proving onlineness for.", + "type": "string" + }, + "hb-proof": { + "$ref": "#/definitions/HbProofFields" + }, + "hb-seed": { + "description": "\\[hbsd\\] HbSeed must be the block seed for the this transaction's firstValid block.", + "type": "string", + "format": "byte" + }, + "hb-vote-id": { + "description": "\\[hbvid\\] HbVoteID must match the HbAddress account's current VoteID.", + "type": "string", + "format": "byte" + }, + "hb-key-dilution": { + "description": "\\[hbkd\\] HbKeyDilution must match HbAddress account's current KeyDilution.", + "type": "integer", + "x-algorand-format": "uint64" + } + } + }, "TransactionAssetTransfer": { "description": "Fields for an asset transfer transaction.\n\nDefinition:\ndata/transactions/asset.go : AssetTransferTxnFields", "type": "object", @@ -2666,6 +2770,37 @@ } } }, + "HbProofFields": { + "description": "\\[hbprf\\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online.", + "type": "object", + "properties": { + "hb-sig": { + "description": "\\[s\\] Signature of the heartbeat message.", + "type": "string", + "format": "byte" + }, + "hb-pk": { + "description": "\\[p\\] Public key of the heartbeat message.", + "type": "string", + "format": "byte" + }, + "hb-pk2": { + "description": "\\[p2\\] Key for new-style two-level ephemeral signature.", + "type": "string", + "format": "byte" + }, + "hb-pk1sig": { + "description": "\\[p1s\\] Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the key PK2.", + "type": "string", + "format": "byte" + }, + "hb-pk2sig": { + "description": "\\[p2s\\] Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master key (OneTimeSignatureVerifier).", + "type": "string", + "format": "byte" + } + } + }, "IndexerStateProofMessage": { "type": "object", "properties": { @@ -2840,18 +2975,29 @@ "type": "string", "x-algorand-format": "Address" }, - "description": "Account(s) marked as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "This parameter is an alias to the `proposers` parameter, it exposes the exact same functionality.", "name": "proposer", "in": "query", "required": false }, + "proposers": { + "type": "array", + "items": { + "type": "string", + "x-algorand-format": "Address" + }, + "description": "Accounts marked as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "name": "proposers", + "in": "query", + "required": false + }, "absent": { "type": "array", "items": { "type": "string", "x-algorand-format": "Address" }, - "description": "Account(s) marked as absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "name": "absent", "in": "query", "required": false @@ -2862,7 +3008,7 @@ "type": "string", "x-algorand-format": "Address" }, - "description": "Account(s) marked as expired in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as expired in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "name": "expired", "in": "query", "required": false @@ -2873,7 +3019,7 @@ "type": "string", "x-algorand-format": "Address" }, - "description": "Account(s) marked as expired or absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as expired or absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "name": "updates", "in": "query", "required": false @@ -2884,7 +3030,7 @@ "type": "string", "x-algorand-format": "Address" }, - "description": "Account(s) marked as expired, absent or as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as expired, absent or as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "name": "participation", "in": "query", "required": false @@ -3092,7 +3238,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string", "name": "tx-type", diff --git a/api/indexer.oas3.yml b/api/indexer.oas3.yml index a7548627b..aec70e739 100644 --- a/api/indexer.oas3.yml +++ b/api/indexer.oas3.yml @@ -2,7 +2,7 @@ "components": { "parameters": { "absent": { - "description": "Account(s) marked as absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "explode": false, "in": "query", "name": "absent", @@ -150,7 +150,7 @@ } }, "expired": { - "description": "Account(s) marked as expired in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as expired in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "explode": false, "in": "query", "name": "expired", @@ -232,7 +232,7 @@ "x-algorand-format": "base64" }, "participation": { - "description": "Account(s) marked as expired, absent or as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as expired, absent or as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "explode": false, "in": "query", "name": "participation", @@ -246,7 +246,7 @@ "style": "form" }, "proposer": { - "description": "Account(s) marked as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "This parameter is an alias to the `proposers` parameter, it exposes the exact same functionality.", "explode": false, "in": "query", "name": "proposer", @@ -259,6 +259,20 @@ }, "style": "form" }, + "proposers": { + "description": "Accounts marked as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "explode": false, + "in": "query", + "name": "proposers", + "schema": { + "items": { + "type": "string", + "x-algorand-format": "Address" + }, + "type": "array" + }, + "style": "form" + }, "rekey-to": { "description": "Include results which include the rekey-to field.", "in": "query", @@ -318,7 +332,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string" } @@ -332,7 +347,7 @@ } }, "updates": { - "description": "Account(s) marked as expired or absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as expired or absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "explode": false, "in": "query", "name": "updates", @@ -1730,6 +1745,42 @@ ], "type": "string" }, + "HbProofFields": { + "description": "\\[hbprf\\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online.", + "properties": { + "hb-pk": { + "description": "\\[p\\] Public key of the heartbeat message.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "hb-pk1sig": { + "description": "\\[p1s\\] Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the key PK2.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "hb-pk2": { + "description": "\\[p2\\] Key for new-style two-level ephemeral signature.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "hb-pk2sig": { + "description": "\\[p2s\\] Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master key (OneTimeSignatureVerifier).", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "hb-sig": { + "description": "\\[s\\] Signature of the heartbeat message.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + } + }, + "type": "object" + }, "HealthCheck": { "description": "A health check response.", "properties": { @@ -2178,6 +2229,9 @@ "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", "type": "string" }, + "heartbeat-transaction": { + "$ref": "#/components/schemas/TransactionHeartbeat" + }, "id": { "description": "Transaction ID", "type": "string" @@ -2259,7 +2313,7 @@ "$ref": "#/components/schemas/TransactionStateProof" }, "tx-type": { - "description": "\\[type\\] Indicates what type of transaction this is. Different types have different fields.\n\nValid types, and where their fields are stored:\n* \\[pay\\] payment-transaction\n* \\[keyreg\\] keyreg-transaction\n* \\[acfg\\] asset-config-transaction\n* \\[axfer\\] asset-transfer-transaction\n* \\[afrz\\] asset-freeze-transaction\n* \\[appl\\] application-transaction\n* \\[stpf\\] state-proof-transaction", + "description": "\\[type\\] Indicates what type of transaction this is. Different types have different fields.\n\nValid types, and where their fields are stored:\n* \\[pay\\] payment-transaction\n* \\[keyreg\\] keyreg-transaction\n* \\[acfg\\] asset-config-transaction\n* \\[axfer\\] asset-transfer-transaction\n* \\[afrz\\] asset-freeze-transaction\n* \\[appl\\] application-transaction\n* \\[stpf\\] state-proof-transaction\n* \\[hb\\] heartbeat-transaction", "enum": [ "pay", "keyreg", @@ -2267,7 +2321,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string", "x-algorand-format": "tx-type-enum" @@ -2425,6 +2480,43 @@ ], "type": "object" }, + "TransactionHeartbeat": { + "description": "Fields for a heartbeat transaction.\n\nDefinition:\ndata/transactions/heartbeat.go : HeartbeatTxnFields", + "properties": { + "hb-address": { + "description": "\\[hbad\\] HbAddress is the account this txn is proving onlineness for.", + "type": "string" + }, + "hb-key-dilution": { + "description": "\\[hbkd\\] HbKeyDilution must match HbAddress account's current KeyDilution.", + "type": "integer", + "x-algorand-format": "uint64" + }, + "hb-proof": { + "$ref": "#/components/schemas/HbProofFields" + }, + "hb-seed": { + "description": "\\[hbsd\\] HbSeed must be the block seed for the this transaction's firstValid block.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "hb-vote-id": { + "description": "\\[hbvid\\] HbVoteID must match the HbAddress account's current VoteID.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + } + }, + "required": [ + "hb-address", + "hb-key-dilution", + "hb-proof", + "hb-seed", + "hb-vote-id" + ], + "type": "object" + }, "TransactionKeyreg": { "description": "Fields for a keyreg transaction.\n\nDefinition:\ndata/transactions/keyreg.go : KeyregTxnFields", "properties": { @@ -2718,7 +2810,7 @@ "x-algorand-format": "Address" }, { - "description": "Deprecated and disallowed. This parameter used to include results for a specified round. Requests with this parameter set are now rejected.", + "description": "Include results for the specified round. For performance reasons, this parameter may be disabled on some configurations. Using application-id or asset-id filters will return both creator and opt-in accounts. Filtering by include-all will return creator and opt-in accounts for deleted assets and accounts. Non-opt-in managers are not included in the results when asset-id is used.", "in": "query", "name": "round", "schema": { @@ -2830,7 +2922,7 @@ } }, { - "description": "Deprecated and disallowed. This parameter used to include results for a specified round. Requests with this parameter set are now rejected.", + "description": "Include results for the specified round.", "in": "query", "name": "round", "schema": { @@ -3605,7 +3697,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string" } @@ -4784,7 +4877,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string" } @@ -5001,9 +5095,234 @@ ] } }, + "/v2/block-headers": { + "get": { + "description": "Search for block headers. Block headers are returned in ascending round order. Transactions are not included in the output.", + "operationId": "searchForBlockHeaders", + "parameters": [ + { + "description": "Maximum number of results to return. There could be additional pages even if the limit is not reached.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "description": "The next page of results. Use the next token provided by the previous results.", + "in": "query", + "name": "next", + "schema": { + "type": "string" + } + }, + { + "description": "Include results at or after the specified min-round.", + "in": "query", + "name": "min-round", + "schema": { + "type": "integer" + } + }, + { + "description": "Include results at or before the specified max-round.", + "in": "query", + "name": "max-round", + "schema": { + "type": "integer" + } + }, + { + "description": "Include results before the given time. Must be an RFC 3339 formatted string.", + "in": "query", + "name": "before-time", + "schema": { + "format": "date-time", + "type": "string", + "x-algorand-format": "RFC3339 String" + }, + "x-algorand-format": "RFC3339 String" + }, + { + "description": "Include results after the given time. Must be an RFC 3339 formatted string.", + "in": "query", + "name": "after-time", + "schema": { + "format": "date-time", + "type": "string", + "x-algorand-format": "RFC3339 String" + }, + "x-algorand-format": "RFC3339 String" + }, + { + "description": "This parameter is an alias to the `proposers` parameter, it exposes the exact same functionality.", + "explode": false, + "in": "query", + "name": "proposer", + "schema": { + "items": { + "type": "string", + "x-algorand-format": "Address" + }, + "type": "array" + }, + "style": "form" + }, + { + "description": "Accounts marked as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "explode": false, + "in": "query", + "name": "proposers", + "schema": { + "items": { + "type": "string", + "x-algorand-format": "Address" + }, + "type": "array" + }, + "style": "form" + }, + { + "description": "Accounts marked as expired in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "explode": false, + "in": "query", + "name": "expired", + "schema": { + "items": { + "type": "string", + "x-algorand-format": "Address" + }, + "type": "array" + }, + "style": "form" + }, + { + "description": "Accounts marked as absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "explode": false, + "in": "query", + "name": "absent", + "schema": { + "items": { + "type": "string", + "x-algorand-format": "Address" + }, + "type": "array" + }, + "style": "form" + }, + { + "description": "Accounts marked as expired or absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "explode": false, + "in": "query", + "name": "updates", + "schema": { + "items": { + "type": "string", + "x-algorand-format": "Address" + }, + "type": "array" + }, + "style": "form" + }, + { + "description": "Accounts marked as expired, absent or as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "explode": false, + "in": "query", + "name": "participation", + "schema": { + "items": { + "type": "string", + "x-algorand-format": "Address" + }, + "type": "array" + }, + "style": "form" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "blocks": { + "items": { + "$ref": "#/components/schemas/BlockHeader" + }, + "type": "array" + }, + "current-round": { + "description": "Round at which the results were computed.", + "type": "integer" + }, + "next-token": { + "description": "Used for pagination, when making another request provide this token with the next parameter.", + "type": "string" + } + }, + "required": [ + "blocks", + "current-round" + ], + "type": "object" + } + } + }, + "description": "(empty)" + }, + "404": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "properties": {}, + "type": "object" + }, + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + }, + "description": "Response for errors" + }, + "500": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "properties": {}, + "type": "object" + }, + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + }, + "description": "Response for errors" + } + }, + "tags": [ + "search" + ] + } + }, "/v2/blocks": { "get": { - "description": "Search for blocks. Blocks are returned in ascending round order. Transactions are not included in the output.", + "description": "This endpoint is an alias for GET /v2/blocks. The behavior is identical.", "operationId": "searchForBlocks", "parameters": [ { @@ -5061,7 +5380,7 @@ "x-algorand-format": "RFC3339 String" }, { - "description": "Account(s) marked as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "This parameter is an alias to the `proposers` parameter, it exposes the exact same functionality.", "explode": false, "in": "query", "name": "proposer", @@ -5075,7 +5394,21 @@ "style": "form" }, { - "description": "Account(s) marked as expired in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "explode": false, + "in": "query", + "name": "proposers", + "schema": { + "items": { + "type": "string", + "x-algorand-format": "Address" + }, + "type": "array" + }, + "style": "form" + }, + { + "description": "Accounts marked as expired in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "explode": false, "in": "query", "name": "expired", @@ -5089,7 +5422,7 @@ "style": "form" }, { - "description": "Account(s) marked as absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "explode": false, "in": "query", "name": "absent", @@ -5103,7 +5436,7 @@ "style": "form" }, { - "description": "Account(s) marked as expired or absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as expired or absent in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "explode": false, "in": "query", "name": "updates", @@ -5117,7 +5450,7 @@ "style": "form" }, { - "description": "Account(s) marked as expired, absent or as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", + "description": "Accounts marked as expired, absent or as proposer in the block header's participation updates. This parameter accepts a comma separated list of addresses.", "explode": false, "in": "query", "name": "participation", @@ -5338,7 +5671,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string" } diff --git a/api/middlewares/pna.go b/api/middlewares/pna.go new file mode 100644 index 000000000..13f565de8 --- /dev/null +++ b/api/middlewares/pna.go @@ -0,0 +1,20 @@ +package middlewares + +import ( + "net/http" + + "github.com/labstack/echo/v4" +) + +// MakePNA constructs the Private Network Access middleware function +func MakePNA() echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(ctx echo.Context) error { + req := ctx.Request() + if req.Method == http.MethodOptions && req.Header.Get("Access-Control-Request-Private-Network") == "true" { + ctx.Response().Header().Set("Access-Control-Allow-Private-Network", "true") + } + return next(ctx) + } + } +} diff --git a/api/middlewares/pna_test.go b/api/middlewares/pna_test.go new file mode 100644 index 000000000..364508bfc --- /dev/null +++ b/api/middlewares/pna_test.go @@ -0,0 +1,69 @@ +package middlewares + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/labstack/echo/v4" + "github.com/stretchr/testify/assert" +) + +func TestMakePNA(t *testing.T) { + // Create a new Echo instance + e := echo.New() + + // Create a handler to be wrapped by the middleware + handler := func(c echo.Context) error { + return c.String(http.StatusOK, "OK") + } + + // Create the middleware + middleware := MakePNA() + + // Test case 1: OPTIONS request with Access-Control-Request-Private-Network header + t.Run("OPTIONS request with PNA header", func(t *testing.T) { + // Create a new HTTP request and response recorder + req := httptest.NewRequest(http.MethodOptions, "/", nil) + rec := httptest.NewRecorder() + + // Set the expected PNA header + req.Header.Set("Access-Control-Request-Private-Network", "true") + + // Create Echo context + c := e.NewContext(req, rec) + + // Call our MakePNA middleware + err := middleware(handler)(c) + + // Assert there's no error and check the PNA header was set correctly + assert.NoError(t, err) + assert.Equal(t, "true", rec.Header().Get("Access-Control-Allow-Private-Network")) + }) + + // Test case 2: Non-OPTIONS request + t.Run("Non-OPTIONS request", func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + + err := middleware(handler)(c) + + // Assert there's no error and check the PNA header wasn't set + assert.NoError(t, err) + assert.Empty(t, rec.Header().Get("Access-Control-Allow-Private-Network")) + }) + + // Test case 3: OPTIONS request without Access-Control-Request-Private-Network header + t.Run("OPTIONS request without Private Network header", func(t *testing.T) { + req := httptest.NewRequest(http.MethodOptions, "/", nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + + err := middleware(handler)(c) + + // Assert there's no error and check the PNA header wasn't set + assert.NoError(t, err) + assert.Empty(t, rec.Header().Get("Access-Control-Allow-Private-Network")) + }) +} diff --git a/api/server.go b/api/server.go index 6a703d24a..1e4decbad 100644 --- a/api/server.go +++ b/api/server.go @@ -18,11 +18,17 @@ import ( "github.com/algorand/indexer/v3/idb" ) -// ExtraOptions are options which change the behavior or the HTTP server. +// ExtraOptions are options which change the behavior of the HTTP server. type ExtraOptions struct { // Tokens are the access tokens which can access the API. Tokens []string + // DeveloperMode turns on features like AddressSearchRoundRewind + DeveloperMode bool + + // Respond to Private Network Access preflight requests sent to the indexer. + EnablePrivateNetworkAccessHeader bool + // MetricsEndpoint turns on the /metrics endpoint for prometheus metrics. MetricsEndpoint bool @@ -45,7 +51,7 @@ type ExtraOptions struct { MaxAPIResourcesPerAccount uint64 // MaxAccountListSize is the maximum number of items that can be passed in query parameter account lists. - // (e.g.: GET /v2/blocks?proposer=A,B,C) + // (e.g.: GET /v2/block-headers?proposers=A,B,C) // // Zero means unlimited. MaxAccountListSize uint64 @@ -111,6 +117,9 @@ func Serve(ctx context.Context, serveAddr string, db idb.IndexerDb, dataError fu } e.Use(middlewares.MakeLogger(log)) + if options.EnablePrivateNetworkAccessHeader { + e.Use(middlewares.MakePNA()) + } e.Use(middleware.CORS()) e.Use(middleware.GzipWithConfig(middleware.GzipConfig{ // we currently support compressed result only for GET /v2/blocks/ API @@ -140,12 +149,13 @@ func Serve(ctx context.Context, serveAddr string, db idb.IndexerDb, dataError fu } api := ServerImplementation{ - db: db, - dataError: dataError, - timeout: options.handlerTimeout(), - log: log, - disabledParams: disabledMap, - opts: options, + EnableAddressSearchRoundRewind: options.DeveloperMode, + db: db, + dataError: dataError, + timeout: options.handlerTimeout(), + log: log, + disabledParams: disabledMap, + opts: options, } generated.RegisterHandlers(e, &api, middleware...) diff --git a/api/test_resources/heartbeat.response b/api/test_resources/heartbeat.response new file mode 100644 index 000000000..5e3840863 --- /dev/null +++ b/api/test_resources/heartbeat.response @@ -0,0 +1,2 @@ +{"close-rewards":0,"closing-amount":0,"confirmed-round":1,"fee":0,"first-valid":106,"genesis-hash":"OW4MK52kEQKHteDrbndB7aFjdP9JvNlI9HYefg2WbDg=","heartbeat-transaction":{"hb-address":"RI53WA75QRYLN64GMKBALH35SDFPEJDW5QMTYU3H2F36DBVAHPDN3FF4YA","hb-key-dilution":100,"hb-proof":{"hb-pk":"xcOVoARNqc58zm7KQrbrSunoTMH+56ISf2BLPX4k5DM=","hb-pk1sig":"KwZl8nfA0linTL+quZ1UF3u0ac0y44nKNvjaUOia8sWGedTWiDr3tElqWTdICVVpvCzn2hIweDaWM1QqREV2BA==","hb-pk2":"ORrVitDsfmg/tlsfRIWu35InLVNSLCNWHPf9cxAz4r0=","hb-pk2sig":"gBcItYg/otjdd3kj0cPKkj6JO+CBeEgjBJ+NGLX4j3kU6u1d8+B8f2hytjkNwmja4w04JZaPzQpP5R8kgqgjDA==","hb-sig":"NvPZHly6M+neFgSXwzsYiL3gav4easw+Nmavp1q/GwWwLIOCX3W7jBzilLHypGymkso/M1RNzUq3O8yMdPA8Cw=="},"hb-seed":"mFwjFbENs+K/0c/ebAMSH2LGcn2YMdJ6gTyo6XF9lfQ=","hb-vote-id":"onzbcE5b1QBnQcWrWWC2g/hE3Wea737jMaap7CSx4ss="},"id":"RCV4LKXW34V6767SD4DBNSE4U4PRBDYKR6NVHZ54FFRVYWDNJV7Q","intra-round-offset":2,"last-valid":116,"receiver-rewards":0,"round-time":1734723734,"sender":"GAU5WA6DT2EPFS6LKOA333BQP67NXIHZ7JPOOHMZWJDPZRL4XMHDDDUCKA","sender-rewards":0,"signature":{},"tx-type":"hb"} + diff --git a/api/test_resources/heartbeat.txn b/api/test_resources/heartbeat.txn new file mode 100644 index 000000000..c20700037 Binary files /dev/null and b/api/test_resources/heartbeat.txn differ diff --git a/api/util_test.go b/api/util_test.go index 95d4ab39a..8fa07b1af 100644 --- a/api/util_test.go +++ b/api/util_test.go @@ -9,9 +9,9 @@ import ( "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/require" - "github.com/algorand/go-algorand-sdk/v2/types" - "github.com/algorand/indexer/v3/idb" + + "github.com/algorand/go-algorand-sdk/v2/types" ) func TestCallWithTimeoutTimesOut(t *testing.T) { diff --git a/cmd/algorand-indexer/daemon.go b/cmd/algorand-indexer/daemon.go index 39fc4888d..faf36608b 100644 --- a/cmd/algorand-indexer/daemon.go +++ b/cmd/algorand-indexer/daemon.go @@ -23,36 +23,37 @@ import ( ) type daemonConfig struct { - flags *pflag.FlagSet - daemonServerAddr string - developerMode bool - metricsMode string - tokenString string - writeTimeout time.Duration - readTimeout time.Duration - maxConn uint32 - maxAPIResourcesPerAccount uint32 - maxAccountListSize uint32 - maxBlocksLimit uint32 - defaultBlocksLimit uint32 - maxTransactionsLimit uint32 - defaultTransactionsLimit uint32 - maxAccountsLimit uint32 - defaultAccountsLimit uint32 - maxAssetsLimit uint32 - defaultAssetsLimit uint32 - maxBoxesLimit uint32 - defaultBoxesLimit uint32 - maxBalancesLimit uint32 - defaultBalancesLimit uint32 - maxApplicationsLimit uint32 - defaultApplicationsLimit uint32 - enableAllParameters bool - indexerDataDir string - cpuProfile string - pidFilePath string - configFile string - suppliedAPIConfigFile string + flags *pflag.FlagSet + daemonServerAddr string + developerMode bool + enablePrivateNetworkAccessHeader bool + metricsMode string + tokenString string + writeTimeout time.Duration + readTimeout time.Duration + maxConn uint32 + maxAPIResourcesPerAccount uint32 + maxAccountListSize uint32 + maxBlocksLimit uint32 + defaultBlocksLimit uint32 + maxTransactionsLimit uint32 + defaultTransactionsLimit uint32 + maxAccountsLimit uint32 + defaultAccountsLimit uint32 + maxAssetsLimit uint32 + defaultAssetsLimit uint32 + maxBoxesLimit uint32 + defaultBoxesLimit uint32 + maxBalancesLimit uint32 + defaultBalancesLimit uint32 + maxApplicationsLimit uint32 + defaultApplicationsLimit uint32 + enableAllParameters bool + indexerDataDir string + cpuProfile string + pidFilePath string + configFile string + suppliedAPIConfigFile string } // DaemonCmd creates the main cobra command, initializes flags, and viper aliases @@ -73,7 +74,8 @@ func DaemonCmd() *cobra.Command { cfg.flags = daemonCmd.Flags() cfg.flags.StringVarP(&cfg.daemonServerAddr, "server", "S", ":8980", "host:port to serve API on (default :8980)") cfg.flags.StringVarP(&cfg.tokenString, "token", "t", "", "an optional auth token, when set REST calls must use this token in a bearer format, or in a 'X-Indexer-API-Token' header") - cfg.flags.BoolVarP(&cfg.developerMode, "dev-mode", "", false, "has no effect currently, reserved for future performance intensive operations") + cfg.flags.BoolVarP(&cfg.developerMode, "dev-mode", "", false, "allow performance intensive operations like searching for accounts at a particular round") + cfg.flags.BoolVarP(&cfg.enablePrivateNetworkAccessHeader, "enable-private-network-access-header", "", false, "respond to Private Network Access preflight requests") cfg.flags.StringVarP(&cfg.metricsMode, "metrics-mode", "", "OFF", "configure the /metrics endpoint to [ON, OFF, VERBOSE]") cfg.flags.DurationVarP(&cfg.writeTimeout, "write-timeout", "", 30*time.Second, "set the maximum duration to wait before timing out writes to a http response, breaking connection") cfg.flags.DurationVarP(&cfg.readTimeout, "read-timeout", "", 5*time.Second, "set the maximum duration for reading the entire request") @@ -306,6 +308,8 @@ func runDaemon(daemonConfig *daemonConfig) error { // makeOptions converts CLI options to server options func makeOptions(daemonConfig *daemonConfig) (options api.ExtraOptions) { + options.EnablePrivateNetworkAccessHeader = daemonConfig.enablePrivateNetworkAccessHeader + options.DeveloperMode = daemonConfig.developerMode if daemonConfig.tokenString != "" { options.Tokens = append(options.Tokens, daemonConfig.tokenString) } diff --git a/cmd/algorand-indexer/daemon_test.go b/cmd/algorand-indexer/daemon_test.go index 62262d3f8..36aa887e4 100644 --- a/cmd/algorand-indexer/daemon_test.go +++ b/cmd/algorand-indexer/daemon_test.go @@ -12,6 +12,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/pflag" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/algorand/indexer/v3/config" "github.com/algorand/indexer/v3/util" @@ -23,7 +24,7 @@ func TestParameterConfigErrorWhenBothFileTypesArePresent(t *testing.T) { indexerDataDir := t.TempDir() for _, configFiletype := range config.FileTypes { autoloadPath := filepath.Join(indexerDataDir, autoLoadParameterConfigFileName+"."+configFiletype) - os.WriteFile(autoloadPath, []byte{}, fs.ModePerm) + require.NoError(t, os.WriteFile(autoloadPath, []byte{}, fs.ModePerm)) } daemonConfig := &daemonConfig{} @@ -41,7 +42,7 @@ func TestIndexerConfigErrorWhenBothFileTypesArePresent(t *testing.T) { indexerDataDir := t.TempDir() for _, configFiletype := range config.FileTypes { autoloadPath := filepath.Join(indexerDataDir, autoLoadIndexerConfigFileName+"."+configFiletype) - os.WriteFile(autoloadPath, []byte{}, fs.ModePerm) + require.NoError(t, os.WriteFile(autoloadPath, []byte{}, fs.ModePerm)) } daemonConfig := &daemonConfig{} @@ -59,7 +60,7 @@ func TestConfigWithEnableAllParamsExpectError(t *testing.T) { for _, configFiletype := range config.FileTypes { indexerDataDir := t.TempDir() autoloadPath := filepath.Join(indexerDataDir, autoLoadIndexerConfigFileName+"."+configFiletype) - os.WriteFile(autoloadPath, []byte{}, fs.ModePerm) + require.NoError(t, os.WriteFile(autoloadPath, []byte{}, fs.ModePerm)) daemonConfig := &daemonConfig{} daemonConfig.flags = pflag.NewFlagSet("indexer", 0) daemonConfig.indexerDataDir = indexerDataDir @@ -88,7 +89,7 @@ func TestConfigInvalidExpectError(t *testing.T) { b := bytes.NewBufferString("") indexerDataDir := t.TempDir() tempConfigFile := indexerDataDir + "/indexer-alt.yml" - os.WriteFile(tempConfigFile, []byte(";;;"), fs.ModePerm) + require.NoError(t, os.WriteFile(tempConfigFile, []byte(";;;"), fs.ModePerm)) daemonConfig := &daemonConfig{} daemonConfig.flags = pflag.NewFlagSet("indexer", 0) daemonConfig.indexerDataDir = indexerDataDir @@ -102,7 +103,7 @@ func TestConfigInvalidExpectError(t *testing.T) { func TestConfigSpecifiedTwiceExpectError(t *testing.T) { indexerDataDir := t.TempDir() tempConfigFile := indexerDataDir + "/indexer.yml" - os.WriteFile(tempConfigFile, []byte{}, fs.ModePerm) + require.NoError(t, os.WriteFile(tempConfigFile, []byte{}, fs.ModePerm)) daemonConfig := &daemonConfig{} daemonConfig.flags = pflag.NewFlagSet("indexer", 0) daemonConfig.indexerDataDir = indexerDataDir @@ -120,7 +121,7 @@ func TestLoadAPIConfigGivenAutoLoadAndUserSuppliedExpectError(t *testing.T) { autoloadPath := filepath.Join(indexerDataDir, autoLoadParameterConfigFileName+"."+configFiletype) userSuppliedPath := filepath.Join(indexerDataDir, "foobar.yml") - os.WriteFile(autoloadPath, []byte{}, fs.ModePerm) + require.NoError(t, os.WriteFile(autoloadPath, []byte{}, fs.ModePerm)) cfg := &daemonConfig{} cfg.indexerDataDir = indexerDataDir cfg.suppliedAPIConfigFile = userSuppliedPath @@ -149,7 +150,7 @@ func TestLoadAPIConfigGivenAutoLoadExpectSuccess(t *testing.T) { indexerDataDir := t.TempDir() autoloadPath := filepath.Join(indexerDataDir, autoLoadParameterConfigFileName+"."+configFiletype) - os.WriteFile(autoloadPath, []byte{}, fs.ModePerm) + require.NoError(t, os.WriteFile(autoloadPath, []byte{}, fs.ModePerm)) cfg := &daemonConfig{} cfg.indexerDataDir = indexerDataDir @@ -180,7 +181,7 @@ func TestIndexerPidFileCreateFailExpectError(t *testing.T) { for _, configFiletype := range config.FileTypes { indexerDataDir := t.TempDir() autoloadPath := filepath.Join(indexerDataDir, autoLoadIndexerConfigFileName+"."+configFiletype) - os.WriteFile(autoloadPath, []byte{}, fs.ModePerm) + require.NoError(t, os.WriteFile(autoloadPath, []byte{}, fs.ModePerm)) invalidDir := filepath.Join(indexerDataDir, "foo", "bar") cfg := &daemonConfig{} diff --git a/cmd/validator/core/validator_test.go b/cmd/validator/core/validator_test.go index deb8b26ef..c7755b956 100644 --- a/cmd/validator/core/validator_test.go +++ b/cmd/validator/core/validator_test.go @@ -7,9 +7,9 @@ import ( "github.com/jarcoal/httpmock" "github.com/stretchr/testify/require" - "github.com/algorand/go-algorand-sdk/v2/types" - "github.com/algorand/indexer/v3/api" + + "github.com/algorand/go-algorand-sdk/v2/types" ) type mockProcessor struct { diff --git a/config/datadir_test.go b/config/datadir_test.go index d252fcc0a..a1d5f40f5 100644 --- a/config/datadir_test.go +++ b/config/datadir_test.go @@ -19,11 +19,11 @@ func TestAlgodArgsForDataDirNetDoesNotExist(t *testing.T) { func TestAlgodArgsForDataDirTokenDoesNotExist(t *testing.T) { dir, err := os.MkdirTemp("", "datadir") if err != nil { - t.Fatalf(err.Error()) + t.Fatal(err.Error()) } err = os.WriteFile(filepath.Join(dir, "algod.net"), []byte("127.0.0.1:8080"), fs.ModePerm) if err != nil { - t.Fatalf(err.Error()) + t.Fatal(err.Error()) } defer os.RemoveAll(dir) _, _, _, err = AlgodArgsForDataDir(dir) @@ -34,15 +34,15 @@ func TestAlgodArgsForDataDirTokenDoesNotExist(t *testing.T) { func TestAlgodArgsForDataDirSuccess(t *testing.T) { dir, err := os.MkdirTemp("", "datadir") if err != nil { - t.Fatalf(err.Error()) + t.Fatal(err.Error()) } err = os.WriteFile(filepath.Join(dir, "algod.net"), []byte("127.0.0.1:8080"), fs.ModePerm) if err != nil { - t.Fatalf(err.Error()) + t.Fatal(err.Error()) } err = os.WriteFile(filepath.Join(dir, "algod.token"), []byte("abc123"), fs.ModePerm) if err != nil { - t.Fatalf(err.Error()) + t.Fatal(err.Error()) } defer os.RemoveAll(dir) netAddr, token, lastmod, err := AlgodArgsForDataDir(dir) diff --git a/e2e_tests/docker/indexer-filtered/docker-compose.yml b/e2e_tests/docker/indexer-filtered/docker-compose.yml index 7d7494502..4a7c1bd16 100644 --- a/e2e_tests/docker/indexer-filtered/docker-compose.yml +++ b/e2e_tests/docker/indexer-filtered/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3' - services: e2e-write: build: diff --git a/e2e_tests/docker/indexer-filtered/e2e-read/Dockerfile b/e2e_tests/docker/indexer-filtered/e2e-read/Dockerfile index 8d319fde7..4828f71fc 100644 --- a/e2e_tests/docker/indexer-filtered/e2e-read/Dockerfile +++ b/e2e_tests/docker/indexer-filtered/e2e-read/Dockerfile @@ -1,4 +1,4 @@ -ARG GO_IMAGE=golang:1.21.10 +ARG GO_IMAGE=golang:1.23.3 FROM $GO_IMAGE RUN echo "Go image: $GO_IMAGE" diff --git a/e2e_tests/docker/indexer-filtered/e2e-write/Dockerfile b/e2e_tests/docker/indexer-filtered/e2e-write/Dockerfile index ecd26f90f..8b820f6c3 100644 --- a/e2e_tests/docker/indexer-filtered/e2e-write/Dockerfile +++ b/e2e_tests/docker/indexer-filtered/e2e-write/Dockerfile @@ -1,4 +1,4 @@ -ARG GO_IMAGE=golang:1.21.10 +ARG GO_IMAGE=golang:1.23.3 FROM $GO_IMAGE ARG CHANNEL=stable ARG CI_E2E_FILENAME diff --git a/e2e_tests/docker/indexer/Dockerfile b/e2e_tests/docker/indexer/Dockerfile index bb10acf8d..50bff4ae1 100644 --- a/e2e_tests/docker/indexer/Dockerfile +++ b/e2e_tests/docker/indexer/Dockerfile @@ -1,4 +1,4 @@ -ARG GO_IMAGE=golang:1.21.10 +ARG GO_IMAGE=golang:1.23.3 FROM $GO_IMAGE ARG CHANNEL=stable diff --git a/e2e_tests/docker/indexer/docker-compose.yml b/e2e_tests/docker/indexer/docker-compose.yml index 96528bf09..6171f0143 100644 --- a/e2e_tests/docker/indexer/docker-compose.yml +++ b/e2e_tests/docker/indexer/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3' - services: e2e: build: diff --git a/e2e_tests/pyproject.toml b/e2e_tests/pyproject.toml index 26a0da0fd..d440b5d17 100644 --- a/e2e_tests/pyproject.toml +++ b/e2e_tests/pyproject.toml @@ -12,7 +12,7 @@ dependencies = [ "msgpack==1.0.4", "py-algorand-sdk==1.17.0", "pytest==6.2.5", - "PyYAML==6.0", + "PyYAML==6.0.2", "setuptools ==70.0.0", ] diff --git a/go.mod b/go.mod index 3c460e5cc..678734b30 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/algorand/indexer/v3 -go 1.21 +go 1.23 -toolchain go1.21.10 +toolchain go1.23.3 require ( github.com/algorand/avm-abi v0.2.0 - github.com/algorand/go-algorand-sdk/v2 v2.6.0 + github.com/algorand/go-algorand-sdk/v2 v2.7.0 github.com/algorand/go-codec/codec v1.1.10 github.com/algorand/oapi-codegen v1.12.0-algorand.0 github.com/davecgh/go-spew v1.1.1 @@ -15,16 +15,16 @@ require ( github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451 github.com/jackc/pgx/v4 v4.18.2 github.com/jarcoal/httpmock v1.2.0 - github.com/labstack/echo-contrib v0.11.0 - github.com/labstack/echo/v4 v4.9.1 - github.com/orlangure/gnomock v0.28.0 + github.com/labstack/echo-contrib v0.17.1 + github.com/labstack/echo/v4 v4.12.0 + github.com/orlangure/gnomock v0.31.0 github.com/pmezard/go-difflib v1.0.0 - github.com/prometheus/client_golang v1.11.1 - github.com/sirupsen/logrus v1.8.1 + github.com/prometheus/client_golang v1.19.0 + github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.3.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.10.0 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.9.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -34,20 +34,21 @@ require ( github.com/Microsoft/go-winio v0.5.2 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect - github.com/distribution/reference v0.5.0 // indirect - github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v24.0.9+incompatible // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v27.3.1+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/invopop/yaml v0.1.0 // indirect @@ -59,42 +60,48 @@ require ( github.com/jackc/pgtype v1.14.0 // indirect github.com/jackc/puddle v1.3.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/kr/pretty v0.3.0 // indirect - github.com/labstack/gommon v0.4.0 // indirect - github.com/lib/pq v1.10.8 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // 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/moby/docker-image-spec v1.3.1 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/pelletier/go-toml v1.9.4 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.26.0 // indirect - github.com/prometheus/procfs v0.6.0 // indirect - github.com/rogpeppe/go-internal v1.8.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.53.0 // indirect + github.com/prometheus/procfs v0.13.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.2.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasttemplate v1.2.1 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.7.0 // indirect - go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect - google.golang.org/protobuf v1.33.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.29.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 88241e1b1..a4ec30d38 100644 --- a/go.sum +++ b/go.sum @@ -51,64 +51,46 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6 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/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= 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/algorand/avm-abi v0.2.0 h1:bkjsG+BOEcxUcnGSALLosmltE0JZdg+ZisXKx0UDX2k= github.com/algorand/avm-abi v0.2.0/go.mod h1:+CgwM46dithy850bpTeHh9MC99zpn2Snirb3QTl2O/g= -github.com/algorand/go-algorand-sdk/v2 v2.6.0 h1:pfL8lloEi26l6PwAFicmPUguWgKpy1eZZTMlQcci5h0= -github.com/algorand/go-algorand-sdk/v2 v2.6.0/go.mod h1:4ayerzjoWChm3kuVhbgFgURTbaYTtlj0c41eP3av5lw= +github.com/algorand/go-algorand-sdk/v2 v2.7.0 h1:ntORjVgXnm+1jqpj55Fv2MbYitxwE9A+xNYopsN5uoA= +github.com/algorand/go-algorand-sdk/v2 v2.7.0/go.mod h1:BkHnK2PuCqzdGPNeWUo5yo6lRjyDZ9QoMN8GIjfijrA= github.com/algorand/go-codec/codec v1.1.10 h1:zmWYU1cp64jQVTOG8Tw8wa+k0VfwgXIPbnDfiVa+5QA= github.com/algorand/go-codec/codec v1.1.10/go.mod h1:YkEx5nmr/zuCeaDYOIhlDg92Lxju8tj2d2NrYqP7g7k= github.com/algorand/oapi-codegen v1.12.0-algorand.0 h1:W9PvED+wAJc+9EeXPONnA+0zE9UhynEqoDs4OgAxKhk= github.com/algorand/oapi-codegen v1.12.0-algorand.0/go.mod h1:tIWJ9K/qrLDVDt5A1p82UmxZIEGxv2X+uoujdhEAL48= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= -github.com/appleboy/gofight/v2 v2.1.2 h1:VOy3jow4vIK8BRQJoC/I9muxyYlJ2yb9ht2hZoS3rf4= -github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/casbin/casbin/v2 v2.31.2/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/chrismcguire/gobberish v0.0.0-20150821175641-1d8adb509a0e h1:CHPYEbz71w8DqJ7DRIq+MXyCQsdibK08vdcQTY4ufas= github.com/chrismcguire/gobberish v0.0.0-20150821175641-1d8adb509a0e/go.mod h1:6Xhs0ZlsRjXLIiSMLKafbZxML/j30pg9Z1priLuha5s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -116,7 +98,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 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/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -130,16 +111,12 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -147,23 +124,14 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= -github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= -github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= +github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= 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= @@ -178,10 +146,8 @@ github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/getkin/kin-openapi v0.107.0 h1:bxhL6QArW7BXQj8NjXfIJQy680NsMKd25nwhvpCXchg= @@ -192,33 +158,32 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 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/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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= @@ -249,9 +214,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 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.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/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= @@ -267,8 +229,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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.6/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/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/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= @@ -289,29 +251,19 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/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/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/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -325,37 +277,28 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 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.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -411,61 +354,45 @@ github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc= github.com/jarcoal/httpmock v1.2.0/go.mod h1:oCoTsnAz4+UoOUIf5lJOWV2QQIW5UoeUI6aM2YnWAZk= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= 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/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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/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.2/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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 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.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/labstack/echo-contrib v0.11.0 h1:/B7meUKBP7AAoSEOrawpSivhFvu7GQG+kDhlzi5v0Wo= -github.com/labstack/echo-contrib v0.11.0/go.mod h1:Hk8Iyxe2GrYR/ch0cbI3BK7ZhR2Y60YEqtkoZilqDOc= -github.com/labstack/echo/v4 v4.3.0/go.mod h1:PvmtTvhVqKDzDQy4d3bWzPjZLzom4iQbAZy2sgZ/qI8= -github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y= -github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= -github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/labstack/echo-contrib v0.17.1 h1:7I/he7ylVKsDUieaGRZ9XxxTYOjfQwVzHzUYrNykfCU= +github.com/labstack/echo-contrib v0.17.1/go.mod h1:SnsCZtwHBAZm5uBSAtQtXQHI3wqEA73hvTn0bYMKnZA= +github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.8 h1:3fdt97i/cwSU83+E0hZTC/Xpc9mTZxc6UWSCRcSbxiE= -github.com/lib/pq v1.10.8/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -474,45 +401,39 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/maxatome/go-testdeep v1.11.0 h1:Tgh5efyCYyJFGUYiT0qxBSIDeXw0F5zSoatlou685kk= github.com/maxatome/go-testdeep v1.11.0/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -525,149 +446,89 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 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/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= -github.com/orlangure/gnomock v0.28.0 h1:3xlGullCJxjWjWGjEXUzvGH1tP6nXL0HY/lHt9w8oC8= -github.com/orlangure/gnomock v0.28.0/go.mod h1:mPcZ4UaVkWrN5pdOkkNWtaWwiTA/4KMME9pH/IHg5Gc= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/orlangure/gnomock v0.31.0 h1:dgjlQ8DYUPMyNwMZJuYBH+/GF+e7h3sloldPzIJF4k4= +github.com/orlangure/gnomock v0.31.0/go.mod h1:RagxeYv3bKi+li9Lio2Faw5t6Mcy4akkeqXzkgAS3w0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= -github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +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.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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.1.0/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.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.25.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= -github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= +github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o= +github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -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.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.10.0 h1:mXH0UwHS4D2HwWZa75im4xIQynLfblmWV7qcWpfv0yk= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 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/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.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= @@ -677,22 +538,15 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +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.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.4.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 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= @@ -700,13 +554,9 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 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= @@ -714,52 +564,61 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -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/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +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/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 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.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -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-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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= @@ -769,7 +628,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 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/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 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= @@ -799,13 +657,9 @@ golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/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= @@ -844,8 +698,8 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= 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= @@ -874,16 +728,13 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -896,16 +747,13 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -920,8 +768,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w 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-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-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -933,7 +779,6 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -941,7 +786,6 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -953,11 +797,13 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -969,26 +815,19 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/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-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= 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/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= -golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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-20190206041539-40960b6deb8e/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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1049,11 +888,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 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= @@ -1087,7 +921,6 @@ google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUb google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 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= @@ -1099,7 +932,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn 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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 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= @@ -1158,15 +990,14 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U= +google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/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= @@ -1190,6 +1021,8 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -1204,33 +1037,24 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba 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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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.3/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.2.8/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= @@ -1240,7 +1064,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 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= @@ -1249,8 +1072,5 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt 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/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/idb/dummy/dummy.go b/idb/dummy/dummy.go index 29552c4c3..459e9d8db 100644 --- a/idb/dummy/dummy.go +++ b/idb/dummy/dummy.go @@ -54,7 +54,7 @@ func (db *dummyIndexerDb) GetBlock(ctx context.Context, round uint64, options id } // Blocks is part of idb.IndexerDB -func (db *dummyIndexerDb) Blocks(ctx context.Context, bf idb.BlockFilter) (<-chan idb.BlockRow, uint64) { +func (db *dummyIndexerDb) BlockHeaders(ctx context.Context, bf idb.BlockHeaderFilter) (<-chan idb.BlockRow, uint64) { return nil, 0 } diff --git a/idb/idb.go b/idb/idb.go index 61c4430e2..e4462743c 100644 --- a/idb/idb.go +++ b/idb/idb.go @@ -205,7 +205,7 @@ type IndexerDb interface { // The next multiple functions return a channel with results as well as the latest round // accounted. - Blocks(ctx context.Context, bf BlockFilter) (<-chan BlockRow, uint64) + BlockHeaders(ctx context.Context, bf BlockHeaderFilter) (<-chan BlockRow, uint64) Transactions(ctx context.Context, tf TransactionFilter) (<-chan TxnRow, uint64) GetAccounts(ctx context.Context, opts AccountQueryOptions) (<-chan AccountRow, uint64) Assets(ctx context.Context, filter AssetsQuery) (<-chan AssetRow, uint64) @@ -228,8 +228,8 @@ type GetBlockOptions struct { MaxTransactionsLimit uint64 } -// BlockFilter is a parameter object with all the block filter options. -type BlockFilter struct { +// BlockHeaderFilter is a parameter object with all the block filter options. +type BlockHeaderFilter struct { Limit uint64 MaxRound *uint64 MinRound *uint64 diff --git a/idb/idb_test.go b/idb/idb_test.go index 0fa7af7a2..51653ce68 100644 --- a/idb/idb_test.go +++ b/idb/idb_test.go @@ -6,9 +6,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - sdk "github.com/algorand/go-algorand-sdk/v2/types" "github.com/algorand/indexer/v3/idb" "github.com/algorand/indexer/v3/util/test" + + sdk "github.com/algorand/go-algorand-sdk/v2/types" ) func TestTxnRowNext(t *testing.T) { diff --git a/idb/mocks/IndexerDb.go b/idb/mocks/IndexerDb.go index e03ad2cc0..f7b03c408 100644 --- a/idb/mocks/IndexerDb.go +++ b/idb/mocks/IndexerDb.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.20.0. DO NOT EDIT. +// Code generated by mockery v2.47.0. DO NOT EDIT. package mocks @@ -22,6 +22,10 @@ type IndexerDb struct { func (_m *IndexerDb) AddBlock(block *types.ValidatedBlock) error { ret := _m.Called(block) + if len(ret) == 0 { + panic("no return value specified for AddBlock") + } + var r0 error if rf, ok := ret.Get(0).(func(*types.ValidatedBlock) error); ok { r0 = rf(block) @@ -36,6 +40,10 @@ func (_m *IndexerDb) AddBlock(block *types.ValidatedBlock) error { func (_m *IndexerDb) AppLocalState(ctx context.Context, filter idb.ApplicationQuery) (<-chan idb.AppLocalStateRow, uint64) { ret := _m.Called(ctx, filter) + if len(ret) == 0 { + panic("no return value specified for AppLocalState") + } + var r0 <-chan idb.AppLocalStateRow var r1 uint64 if rf, ok := ret.Get(0).(func(context.Context, idb.ApplicationQuery) (<-chan idb.AppLocalStateRow, uint64)); ok { @@ -62,6 +70,10 @@ func (_m *IndexerDb) AppLocalState(ctx context.Context, filter idb.ApplicationQu func (_m *IndexerDb) ApplicationBoxes(ctx context.Context, filter idb.ApplicationBoxQuery) (<-chan idb.ApplicationBoxRow, uint64) { ret := _m.Called(ctx, filter) + if len(ret) == 0 { + panic("no return value specified for ApplicationBoxes") + } + var r0 <-chan idb.ApplicationBoxRow var r1 uint64 if rf, ok := ret.Get(0).(func(context.Context, idb.ApplicationBoxQuery) (<-chan idb.ApplicationBoxRow, uint64)); ok { @@ -88,6 +100,10 @@ func (_m *IndexerDb) ApplicationBoxes(ctx context.Context, filter idb.Applicatio func (_m *IndexerDb) Applications(ctx context.Context, filter idb.ApplicationQuery) (<-chan idb.ApplicationRow, uint64) { ret := _m.Called(ctx, filter) + if len(ret) == 0 { + panic("no return value specified for Applications") + } + var r0 <-chan idb.ApplicationRow var r1 uint64 if rf, ok := ret.Get(0).(func(context.Context, idb.ApplicationQuery) (<-chan idb.ApplicationRow, uint64)); ok { @@ -114,6 +130,10 @@ func (_m *IndexerDb) Applications(ctx context.Context, filter idb.ApplicationQue func (_m *IndexerDb) AssetBalances(ctx context.Context, abq idb.AssetBalanceQuery) (<-chan idb.AssetBalanceRow, uint64) { ret := _m.Called(ctx, abq) + if len(ret) == 0 { + panic("no return value specified for AssetBalances") + } + var r0 <-chan idb.AssetBalanceRow var r1 uint64 if rf, ok := ret.Get(0).(func(context.Context, idb.AssetBalanceQuery) (<-chan idb.AssetBalanceRow, uint64)); ok { @@ -140,6 +160,10 @@ func (_m *IndexerDb) AssetBalances(ctx context.Context, abq idb.AssetBalanceQuer func (_m *IndexerDb) Assets(ctx context.Context, filter idb.AssetsQuery) (<-chan idb.AssetRow, uint64) { ret := _m.Called(ctx, filter) + if len(ret) == 0 { + panic("no return value specified for Assets") + } + var r0 <-chan idb.AssetRow var r1 uint64 if rf, ok := ret.Get(0).(func(context.Context, idb.AssetsQuery) (<-chan idb.AssetRow, uint64)); ok { @@ -162,6 +186,36 @@ func (_m *IndexerDb) Assets(ctx context.Context, filter idb.AssetsQuery) (<-chan return r0, r1 } +// BlockHeaders provides a mock function with given fields: ctx, bf +func (_m *IndexerDb) BlockHeaders(ctx context.Context, bf idb.BlockHeaderFilter) (<-chan idb.BlockRow, uint64) { + ret := _m.Called(ctx, bf) + + if len(ret) == 0 { + panic("no return value specified for BlockHeaders") + } + + var r0 <-chan idb.BlockRow + var r1 uint64 + if rf, ok := ret.Get(0).(func(context.Context, idb.BlockHeaderFilter) (<-chan idb.BlockRow, uint64)); ok { + return rf(ctx, bf) + } + if rf, ok := ret.Get(0).(func(context.Context, idb.BlockHeaderFilter) <-chan idb.BlockRow); ok { + r0 = rf(ctx, bf) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan idb.BlockRow) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, idb.BlockHeaderFilter) uint64); ok { + r1 = rf(ctx, bf) + } else { + r1 = ret.Get(1).(uint64) + } + + return r0, r1 +} + // Close provides a mock function with given fields: func (_m *IndexerDb) Close() { _m.Called() @@ -171,6 +225,10 @@ func (_m *IndexerDb) Close() { func (_m *IndexerDb) DeleteTransactions(ctx context.Context, keep uint64) error { ret := _m.Called(ctx, keep) + if len(ret) == 0 { + panic("no return value specified for DeleteTransactions") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, uint64) error); ok { r0 = rf(ctx, keep) @@ -185,6 +243,10 @@ func (_m *IndexerDb) DeleteTransactions(ctx context.Context, keep uint64) error func (_m *IndexerDb) GetAccounts(ctx context.Context, opts idb.AccountQueryOptions) (<-chan idb.AccountRow, uint64) { ret := _m.Called(ctx, opts) + if len(ret) == 0 { + panic("no return value specified for GetAccounts") + } + var r0 <-chan idb.AccountRow var r1 uint64 if rf, ok := ret.Get(0).(func(context.Context, idb.AccountQueryOptions) (<-chan idb.AccountRow, uint64)); ok { @@ -211,6 +273,10 @@ func (_m *IndexerDb) GetAccounts(ctx context.Context, opts idb.AccountQueryOptio func (_m *IndexerDb) GetBlock(ctx context.Context, round uint64, options idb.GetBlockOptions) (v2types.BlockHeader, []idb.TxnRow, error) { ret := _m.Called(ctx, round, options) + if len(ret) == 0 { + panic("no return value specified for GetBlock") + } + var r0 v2types.BlockHeader var r1 []idb.TxnRow var r2 error @@ -244,6 +310,10 @@ func (_m *IndexerDb) GetBlock(ctx context.Context, round uint64, options idb.Get func (_m *IndexerDb) GetNetworkState() (idb.NetworkState, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetNetworkState") + } + var r0 idb.NetworkState var r1 error if rf, ok := ret.Get(0).(func() (idb.NetworkState, error)); ok { @@ -268,6 +338,10 @@ func (_m *IndexerDb) GetNetworkState() (idb.NetworkState, error) { func (_m *IndexerDb) GetNextRoundToAccount() (uint64, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetNextRoundToAccount") + } + var r0 uint64 var r1 error if rf, ok := ret.Get(0).(func() (uint64, error)); ok { @@ -292,6 +366,10 @@ func (_m *IndexerDb) GetNextRoundToAccount() (uint64, error) { func (_m *IndexerDb) GetSpecialAccounts(ctx context.Context) (types.SpecialAddresses, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetSpecialAccounts") + } + var r0 types.SpecialAddresses var r1 error if rf, ok := ret.Get(0).(func(context.Context) (types.SpecialAddresses, error)); ok { @@ -316,6 +394,10 @@ func (_m *IndexerDb) GetSpecialAccounts(ctx context.Context) (types.SpecialAddre func (_m *IndexerDb) Health(ctx context.Context) (idb.Health, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Health") + } + var r0 idb.Health var r1 error if rf, ok := ret.Get(0).(func(context.Context) (idb.Health, error)); ok { @@ -340,6 +422,10 @@ func (_m *IndexerDb) Health(ctx context.Context) (idb.Health, error) { func (_m *IndexerDb) LoadGenesis(genesis v2types.Genesis) error { ret := _m.Called(genesis) + if len(ret) == 0 { + panic("no return value specified for LoadGenesis") + } + var r0 error if rf, ok := ret.Get(0).(func(v2types.Genesis) error); ok { r0 = rf(genesis) @@ -354,6 +440,10 @@ func (_m *IndexerDb) LoadGenesis(genesis v2types.Genesis) error { func (_m *IndexerDb) SetNetworkState(genesis v2types.Digest) error { ret := _m.Called(genesis) + if len(ret) == 0 { + panic("no return value specified for SetNetworkState") + } + var r0 error if rf, ok := ret.Get(0).(func(v2types.Digest) error); ok { r0 = rf(genesis) @@ -368,6 +458,10 @@ func (_m *IndexerDb) SetNetworkState(genesis v2types.Digest) error { func (_m *IndexerDb) Transactions(ctx context.Context, tf idb.TransactionFilter) (<-chan idb.TxnRow, uint64) { ret := _m.Called(ctx, tf) + if len(ret) == 0 { + panic("no return value specified for Transactions") + } + var r0 <-chan idb.TxnRow var r1 uint64 if rf, ok := ret.Get(0).(func(context.Context, idb.TransactionFilter) (<-chan idb.TxnRow, uint64)); ok { @@ -390,13 +484,12 @@ func (_m *IndexerDb) Transactions(ctx context.Context, tf idb.TransactionFilter) return r0, r1 } -type mockConstructorTestingTNewIndexerDb interface { +// NewIndexerDb creates a new instance of IndexerDb. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIndexerDb(t interface { mock.TestingT Cleanup(func()) -} - -// NewIndexerDb creates a new instance of IndexerDb. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewIndexerDb(t mockConstructorTestingTNewIndexerDb) *IndexerDb { +}) *IndexerDb { mock := &IndexerDb{} mock.Mock.Test(t) diff --git a/idb/postgres/internal/encoding/encoding_test.go b/idb/postgres/internal/encoding/encoding_test.go index 50ca9ff76..8b8e1b6ed 100644 --- a/idb/postgres/internal/encoding/encoding_test.go +++ b/idb/postgres/internal/encoding/encoding_test.go @@ -4,14 +4,15 @@ import ( "fmt" "testing" - "github.com/algorand/go-algorand-sdk/v2/encoding/msgpack" - sdk "github.com/algorand/go-algorand-sdk/v2/types" - itypes "github.com/algorand/indexer/v3/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algorand/indexer/v3/idb" "github.com/algorand/indexer/v3/idb/postgres/internal/types" + itypes "github.com/algorand/indexer/v3/types" + + "github.com/algorand/go-algorand-sdk/v2/encoding/msgpack" + sdk "github.com/algorand/go-algorand-sdk/v2/types" ) func TestEncodeSignedTxnWithAD(t *testing.T) { @@ -42,7 +43,7 @@ func TestEncodeSignedTxnWithAD(t *testing.T) { var stxn sdk.SignedTxnWithAD for _, mt := range testTxns { t.Run(mt.name, func(t *testing.T) { - msgpack.Decode(mt.msgpack, &stxn) + require.NoError(t, msgpack.Decode(mt.msgpack, &stxn)) js := EncodeSignedTxnWithAD(stxn) require.Equal(t, mt.json, string(js)) }) diff --git a/idb/postgres/internal/migrations/convert_account_data/m_test.go b/idb/postgres/internal/migrations/convert_account_data/m_test.go index a00fd27c5..f4a97fb39 100644 --- a/idb/postgres/internal/migrations/convert_account_data/m_test.go +++ b/idb/postgres/internal/migrations/convert_account_data/m_test.go @@ -5,7 +5,6 @@ import ( "fmt" "testing" - sdk "github.com/algorand/go-algorand-sdk/v2/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -16,6 +15,8 @@ import ( cad "github.com/algorand/indexer/v3/idb/postgres/internal/migrations/convert_account_data" pgtest "github.com/algorand/indexer/v3/idb/postgres/internal/testing" pgutil "github.com/algorand/indexer/v3/idb/postgres/internal/util" + + sdk "github.com/algorand/go-algorand-sdk/v2/types" ) func makeAddress(i int) sdk.Address { diff --git a/idb/postgres/internal/writer/write_txn.go b/idb/postgres/internal/writer/write_txn.go index 4a6be5f0b..97f819fbe 100644 --- a/idb/postgres/internal/writer/write_txn.go +++ b/idb/postgres/internal/writer/write_txn.go @@ -28,7 +28,7 @@ func transactionAssetID(stxnad *types.SignedTxnWithAD, intra uint, block *types. case types.ApplicationCallTx: assetid = uint64(stxnad.Txn.ApplicationID) if assetid == 0 { - assetid = uint64(stxnad.ApplyData.ApplicationID) + assetid = stxnad.ApplyData.ApplicationID } if assetid == 0 { if block == nil { @@ -42,7 +42,7 @@ func transactionAssetID(stxnad *types.SignedTxnWithAD, intra uint, block *types. case types.AssetConfigTx: assetid = uint64(stxnad.Txn.ConfigAsset) if assetid == 0 { - assetid = uint64(stxnad.ApplyData.ConfigAsset) + assetid = stxnad.ApplyData.ConfigAsset } if assetid == 0 { if block == nil { @@ -112,6 +112,7 @@ func yieldInnerTransactions(ctx context.Context, stxnad *types.SignedTxnWithAD, // Writes database rows for transactions (including inner transactions) to `outCh`. func yieldTransactions(ctx context.Context, block *types.Block, modifiedTxns []types.SignedTxnInBlock, outCh chan []interface{}) error { intra := uint(0) + for idx, stib := range block.Payset { var stxnad types.SignedTxnWithAD var err error @@ -123,7 +124,7 @@ func yieldTransactions(ctx context.Context, block *types.Block, modifiedTxns []t } txn := &stxnad.Txn - typeenum, ok := idb.GetTypeEnum(types.TxType(txn.Type)) + typeenum, ok := idb.GetTypeEnum(txn.Type) if !ok { return fmt.Errorf("yieldTransactions() get type enum") } @@ -153,9 +154,53 @@ func yieldTransactions(ctx context.Context, block *types.Block, modifiedTxns []t } } + if block.ProposerPayout > 0 { + stxnad := SignedTransactionFromBlockPayout(block) + typeenum, ok := idb.GetTypeEnum(stxnad.Txn.Type) + if !ok { + return fmt.Errorf("yieldTransactions() ProposerPayout get type enum - should NEVER happen") + } + id := crypto.TransactionIDString(stxnad.Txn) + extra := idb.TxnExtra{} + row := []interface{}{ + uint64(block.Round), intra, int(typeenum), 0, id, + encoding.EncodeSignedTxnWithAD(stxnad), + encoding.EncodeTxnExtra(&extra)} + select { + case <-ctx.Done(): + return fmt.Errorf("yieldTransactions() ProposerPayout ctx.Err(): %w", ctx.Err()) + case outCh <- row: + } + } + return nil } +// SignedTransactionFromBlockPayout creates a synthetic transaction for the proposer payout. +func SignedTransactionFromBlockPayout(block *types.Block) types.SignedTxnWithAD { + stxnad := types.SignedTxnWithAD{ + SignedTxn: types.SignedTxn{ + Txn: types.Transaction{ + Type: types.PaymentTx, + Header: types.Header{ + Sender: block.FeeSink, + Note: []byte("ProposerPayout for Round " + fmt.Sprint(block.Round)), + FirstValid: block.Round, + LastValid: block.Round, + GenesisID: block.GenesisID, + GenesisHash: block.GenesisHash, + }, + PaymentTxnFields: types.PaymentTxnFields{ + Receiver: block.Proposer, + Amount: block.ProposerPayout, + }, + }, + }, + } + + return stxnad +} + // AddTransactions adds transactions from `block` to the database. // `modifiedTxns` contains enhanced apply data generated by evaluator. func AddTransactions(block *types.Block, modifiedTxns []types.SignedTxnInBlock, tx pgx.Tx) error { diff --git a/idb/postgres/internal/writer/write_txn_participation.go b/idb/postgres/internal/writer/write_txn_participation.go index 65edf1c1c..d458d8548 100644 --- a/idb/postgres/internal/writer/write_txn_participation.go +++ b/idb/postgres/internal/writer/write_txn_participation.go @@ -125,6 +125,14 @@ func AddTransactionParticipation(block *types.Block, tx pgx.Tx) error { next, rows = addInnerTransactionParticipation(&stxnib.SignedTxnWithAD, uint64(block.Round), next+1, rows) } + if block.ProposerPayout > 0 { + // FeeSink is the sender, Proposer is the receiver. + participants := []types.Address{block.FeeSink, block.Proposer} + for j := range participants { + rows = append(rows, []interface{}{participants[j][:], uint64(block.Round), next}) + } + } + _, err := tx.CopyFrom( context.Background(), pgx.Identifier{"txn_participation"}, diff --git a/idb/postgres/internal/writer/writer_test.go b/idb/postgres/internal/writer/writer_test.go index d793b97a4..e0edcbfb8 100644 --- a/idb/postgres/internal/writer/writer_test.go +++ b/idb/postgres/internal/writer/writer_test.go @@ -168,7 +168,7 @@ func TestWriterSpecialAccounts(t *testing.T) { assert.Equal(t, expected, accounts) } -func TestWriterTxnTableBasic(t *testing.T) { +func TestWriterTxnTableBasicNoPayout(t *testing.T) { db, _, shutdownFunc := pgtest.SetupPostgresWithSchema(t) defer shutdownFunc() @@ -255,6 +255,116 @@ func TestWriterTxnTableBasic(t *testing.T) { assert.NoError(t, rows.Err()) } +func TestWriterTxnTableBasicWithProposalPayout(t *testing.T) { + db, _, shutdownFunc := pgtest.SetupPostgresWithSchema(t) + defer shutdownFunc() + + block := sdk.Block{ + BlockHeader: sdk.BlockHeader{ + Round: sdk.Round(2), + TimeStamp: 333, + GenesisID: test.MakeGenesis().ID(), + GenesisHash: test.MakeGenesis().Hash(), + RewardsState: sdk.RewardsState{ + FeeSink: test.FeeAddr, + RewardsLevel: 111111, + }, + TxnCounter: 9, + UpgradeState: sdk.UpgradeState{ + CurrentProtocol: "future", + }, + Proposer: test.AccountE, + ProposerPayout: 2000000, + }, + Payset: make([]sdk.SignedTxnInBlock, 2), + } + + stxnad0 := test.MakePaymentTxn( + 1000, 1, 0, 0, 0, 0, sdk.Address(test.AccountA), sdk.Address(test.AccountB), sdk.Address{}, + sdk.Address{}) + var err error + block.Payset[0], err = + util.EncodeSignedTxn(block.BlockHeader, stxnad0.SignedTxn, stxnad0.ApplyData) + require.NoError(t, err) + + stxnad1 := test.MakeAssetConfigTxn( + 0, 100, 1, false, "ma", "myasset", "myasset.com", sdk.Address(test.AccountA)) + block.Payset[1], err = + util.EncodeSignedTxn(block.BlockHeader, stxnad1.SignedTxn, stxnad1.ApplyData) + require.NoError(t, err) + + f := func(tx pgx.Tx) error { + return writer.AddTransactions(&block, block.Payset, tx) + } + err = pgutil.TxWithRetry(db, serializable, f, nil) + require.NoError(t, err) + + rows, err := db.Query(context.Background(), "SELECT * FROM txn ORDER BY intra") + require.NoError(t, err) + defer rows.Close() + + var round uint64 + var intra uint64 + var typeenum uint + var asset uint64 + var txid []byte + var txn []byte + var extra []byte + + require.True(t, rows.Next()) + err = rows.Scan(&round, &intra, &typeenum, &asset, &txid, &txn, &extra) + require.NoError(t, err) + assert.Equal(t, block.Round, sdk.Round(round)) + assert.Equal(t, uint64(0), intra) + assert.Equal(t, idb.TypeEnumPay, idb.TxnTypeEnum(typeenum)) + assert.Equal(t, uint64(0), asset) + assert.Equal(t, crypto2.TransactionIDString(stxnad0.Txn), string(txid)) + { + stxn, err := encoding.DecodeSignedTxnWithAD(txn) + require.NoError(t, err) + assert.Equal(t, stxnad0, stxn) + } + assert.Equal(t, "{}", string(extra)) + + require.True(t, rows.Next()) + err = rows.Scan(&round, &intra, &typeenum, &asset, &txid, &txn, &extra) + require.NoError(t, err) + assert.Equal(t, block.Round, sdk.Round(round)) + assert.Equal(t, uint64(1), intra) + assert.Equal(t, idb.TypeEnumAssetConfig, idb.TxnTypeEnum(typeenum)) + assert.Equal(t, uint64(9), asset) + assert.Equal(t, crypto2.TransactionIDString(stxnad1.Txn), string(txid)) + { + stxn, err := encoding.DecodeSignedTxnWithAD(txn) + require.NoError(t, err) + assert.Equal(t, stxnad1, stxn) + } + assert.Equal(t, "{}", string(extra)) + + // Payout should be the last transaction. + require.True(t, rows.Next()) + err = rows.Scan(&round, &intra, &typeenum, &asset, &txid, &txn, &extra) + require.NoError(t, err) + + assert.Equal(t, block.Round, sdk.Round(round)) + assert.Equal(t, uint64(2), intra) + assert.Equal(t, idb.TypeEnumPay, idb.TxnTypeEnum(typeenum)) + assert.Equal(t, uint64(0), asset) + + // Intentionally using synthetic payout transaction logic; we are testing insertion and validity + payoutTxn := writer.SignedTransactionFromBlockPayout(&block) + assert.Equal(t, crypto2.TransactionIDString(payoutTxn.Txn), string(txid)) + { + stxn, err := encoding.DecodeSignedTxnWithAD(txn) + require.NoError(t, err) + assert.Equal(t, payoutTxn, stxn) + } + assert.Equal(t, "{}", string(extra)) + + assert.False(t, rows.Next()) + assert.NoError(t, rows.Err()) +} + // Test that asset close amount is written even if it is missing in the apply data // in the block (it is present in the "modified transactions"). func TestWriterTxnTableAssetCloseAmount(t *testing.T) { @@ -314,7 +424,7 @@ func TestWriterTxnTableAssetCloseAmount(t *testing.T) { assert.NoError(t, rows.Err()) } -func TestWriterTxnParticipationTable(t *testing.T) { +func TestWriterTxnParticipationTableNoPayout(t *testing.T) { type testtype struct { name string payset sdk.Payset @@ -425,6 +535,144 @@ func TestWriterTxnParticipationTable(t *testing.T) { } } +func TestWriterTxnParticipationTableWithPayout(t *testing.T) { + type testtype struct { + name string + payset sdk.Payset + expected []txnParticipationRow + } + + makeBlockFunc := func() sdk.Block { + return sdk.Block{ + BlockHeader: sdk.BlockHeader{ + Round: sdk.Round(2), + GenesisID: test.MakeGenesis().ID(), + GenesisHash: test.MakeGenesis().Hash(), + RewardsState: sdk.RewardsState{ + FeeSink: test.FeeAddr, + }, + UpgradeState: sdk.UpgradeState{ + CurrentProtocol: "future", + }, + Proposer: test.AccountE, + ProposerPayout: 2000000, + }, + } + } + + var tests []testtype + { + stxnad0 := test.MakePaymentTxn( + 1000, 1, 0, 0, 0, 0, sdk.Address(test.AccountA), sdk.Address(test.AccountB), sdk.Address{}, + sdk.Address{}) + stib0, err := util.EncodeSignedTxn(makeBlockFunc().BlockHeader, stxnad0.SignedTxn, stxnad0.ApplyData) + require.NoError(t, err) + + stxnad1 := test.MakeAssetConfigTxn( + 0, 100, 1, false, "ma", "myasset", "myasset.com", sdk.Address(test.AccountC)) + stib1, err := util.EncodeSignedTxn(makeBlockFunc().BlockHeader, stxnad1.SignedTxn, stxnad1.ApplyData) + require.NoError(t, err) + + testcase := testtype{ + name: "basic", + payset: []sdk.SignedTxnInBlock{stib0, stib1}, + expected: []txnParticipationRow{ + { + addr: test.AccountA, + round: 2, + intra: 0, + }, + { + addr: test.AccountB, + round: 2, + intra: 0, + }, + { + addr: test.AccountC, + round: 2, + intra: 1, + }, + // Payout involved accounts + { + addr: test.AccountE, + round: 2, + intra: 2, + }, + { + addr: test.FeeAddr, + round: 2, + intra: 2, + }, + }, + } + tests = append(tests, testcase) + } + { + stxnad := test.MakeCreateAppTxn(sdk.Address(test.AccountA)) + stxnad.Txn.ApplicationCallTxnFields.Accounts = + []sdk.Address{sdk.Address(test.AccountB), sdk.Address(test.AccountC)} + stib, err := util.EncodeSignedTxn(makeBlockFunc().BlockHeader, stxnad.SignedTxn, stxnad.ApplyData) + require.NoError(t, err) + + testcase := testtype{ + name: "app_call_addresses", + payset: []sdk.SignedTxnInBlock{stib}, + expected: []txnParticipationRow{ + { + addr: sdk.Address(test.AccountA), + round: 2, + intra: 0, + }, + { + addr: sdk.Address(test.AccountB), + round: 2, + intra: 0, + }, + { + addr: sdk.Address(test.AccountC), + round: 2, + intra: 0, + }, + // Payout involved accounts + { + addr: test.AccountE, + round: 2, + intra: 1, + }, + { + addr: test.FeeAddr, + round: 2, + intra: 1, + }, + }, + } + tests = append(tests, testcase) + } + + for _, testcase := range tests { + t.Run(testcase.name, func(t *testing.T) { + db, _, shutdownFunc := pgtest.SetupPostgresWithSchema(t) + defer shutdownFunc() + + block := makeBlockFunc() + block.Payset = testcase.payset + + f := func(tx pgx.Tx) error { + return writer.AddTransactionParticipation(&block, tx) + } + err := pgutil.TxWithRetry(db, serializable, f, nil) + require.NoError(t, err) + + results, err := txnParticipationQuery( + db, `SELECT * FROM txn_participation ORDER BY round, intra, addr`) + assert.NoError(t, err) + + // Verify expected participation + assert.Equal(t, testcase.expected, results) + }) + } +} + // Create a new account and then delete it. func TestWriterAccountTableBasic(t *testing.T) { db, _, shutdownFunc := pgtest.SetupPostgresWithSchema(t) diff --git a/idb/postgres/postgres.go b/idb/postgres/postgres.go index ce3723b4e..7e8b96e88 100644 --- a/idb/postgres/postgres.go +++ b/idb/postgres/postgres.go @@ -710,8 +710,9 @@ func buildTransactionQuery(tf idb.TransactionFilter) (query string, whereArgs [] whereStr := strings.Join(whereParts, " AND ") query += " WHERE " + whereStr } + if joinParticipation { - // this should match the index on txn_particpation + // this should match the index on txn_participation query += " ORDER BY p.addr, p.round DESC, p.intra DESC" } else { // this should explicitly match the primary key on txn (round,intra) @@ -723,214 +724,6 @@ func buildTransactionQuery(tf idb.TransactionFilter) (query string, whereArgs [] return } -// buildBlockFilters generates filters based on a block's round and/or timestamp. -// -// Filters related to participation are generated elsewhere. -// -// Some of the filters are meant to be used as boolean conditions in the WHERE clause. -// Others, for performance reasons, have to be used as INNER JOIN terms in the FROM clause. -func buildBlockFilters(bf idb.BlockFilter) (whereTerms []string, joinTerms []string) { - - // Round-based filters - if bf.MaxRound != nil { - whereTerms = append( - whereTerms, - fmt.Sprintf("bh.round <= %d", *bf.MaxRound), - ) - } - if bf.MinRound != nil { - whereTerms = append( - whereTerms, - fmt.Sprintf("bh.round >= %d", *bf.MinRound), - ) - } - - // Timestamp-based filters - // - // Converting the timestamp into a round usually results in faster execution plans - // (compared to the execution plans that would result from using the `block_header.realtime` column directly) - // - // Unfortunately, writing this condition in the WHERE clause results in poor execution plans. - // Expressing the filter as an INNER JOIN results in an efficient query execution plan. - if !bf.AfterTime.IsZero() { - tmpl := ` - INNER JOIN ( - SELECT COALESCE(tmp.round, 0) AS round - FROM block_header tmp - WHERE tmp.realtime > (to_timestamp(%d) AT TIME ZONE 'UTC') - ORDER BY tmp.realtime ASC, tmp.round ASC - LIMIT 1 - ) bh_at ON bh.round >= bh_at.round - ` - joinTerms = append( - joinTerms, - fmt.Sprintf(tmpl, bf.AfterTime.UTC().Unix()), - ) - } - if !bf.BeforeTime.IsZero() { - tmpl := ` - INNER JOIN ( - SELECT COALESCE(tmp.round, 0) AS round - FROM block_header tmp - WHERE tmp.realtime < (to_timestamp(%d) AT TIME ZONE 'UTC') - ORDER BY tmp.realtime DESC, tmp.round DESC - LIMIT 1 - ) bh_bt ON bh.round <= bh_bt.round - ` - joinTerms = append( - joinTerms, - fmt.Sprintf(tmpl, bf.BeforeTime.UTC().Unix()), - ) - } - - return whereTerms, joinTerms -} - -func buildBlockQuery(bf idb.BlockFilter) (query string, err error) { - - // helper function to build CTEs - buildCte := func(cteName string, whereTerms []string, joinTerms []string) string { - tmpl := `%s AS ( - SELECT bh.round, bh.header - FROM block_header bh - %s - WHERE %s - ORDER BY bh.round ASC - LIMIT %d - )` - return fmt.Sprintf(tmpl, cteName, strings.Join(joinTerms, "\n"), strings.Join(whereTerms, " AND "), bf.Limit) - } - - // Build auxiliary CTEs for participation-related parameters. - // - // Using CTEs in this way turned out to be necessary to lead CockroachDB's query optimizer - // into using the execution plan we want. - // - // If we were to put the CTE filters in the main query's WHERE clause, that would result - // in a sub-optimal execution plan. At least this was the case at the time of writing. - var CTEs []string - var CteNames []string - { - if len(bf.Proposers) > 0 { - whereTerms, joinTerms := buildBlockFilters(bf) - var proposersStr []string - for addr := range bf.Proposers { - proposersStr = append(proposersStr, `'"`+addr.String()+`"'`) - } - whereTerms = append( - whereTerms, - fmt.Sprintf("( (bh.header->'prp') IS NOT NULL AND ((bh.header->'prp')::TEXT IN (%s)) )", strings.Join(proposersStr, ",")), - ) - - cteName := "prp" - cte := buildCte(cteName, whereTerms, joinTerms) - CTEs = append(CTEs, cte) - CteNames = append(CteNames, cteName) - - } - if len(bf.ExpiredParticipationAccounts) > 0 { - whereTerms, joinTerms := buildBlockFilters(bf) - var expiredStr []string - for addr := range bf.ExpiredParticipationAccounts { - expiredStr = append(expiredStr, `'`+addr.String()+`'`) - } - whereTerms = append( - whereTerms, - fmt.Sprintf("( (bh.header->'partupdrmv') IS NOT NULL AND (bh.header->'partupdrmv') ?| array[%s] )", strings.Join(expiredStr, ",")), - ) - - cteName := "expired" - CTE := buildCte(cteName, whereTerms, joinTerms) - CTEs = append(CTEs, CTE) - CteNames = append(CteNames, "expired") - - } - if len(bf.AbsentParticipationAccounts) > 0 { - whereTerms, joinTerms := buildBlockFilters(bf) - var absentStr []string - for addr := range bf.AbsentParticipationAccounts { - absentStr = append(absentStr, `'`+addr.String()+`'`) - } - whereTerms = append( - whereTerms, - fmt.Sprintf("( (bh.header->'partupdabs') IS NOT NULL AND (bh.header->'partupdabs') ?| array[%s] )", strings.Join(absentStr, ",")), - ) - - cteName := "absent" - CTE := buildCte(cteName, whereTerms, joinTerms) - CTEs = append(CTEs, CTE) - CteNames = append(CteNames, cteName) - } - if len(CteNames) > 0 { - var selects []string - for _, cteName := range CteNames { - selects = append(selects, fmt.Sprintf("SELECT * FROM %s", cteName)) - } - CTE := "tmp AS (" + strings.Join(selects, " UNION ") + ")" - CTEs = append(CTEs, CTE) - } - } - - // Build the main query. It uses the CTEs, if any. - { - var withClause string - if len(CTEs) > 0 { - withClause = "WITH " + strings.Join(CTEs, ",\n") - } - - var fromTable string - if len(CTEs) > 0 { - fromTable = "tmp bh" - } else { - fromTable = "block_header bh" - } - - var whereClause string - var joinClause string - if len(CTEs) == 0 { - whereTerms, joinTerms := buildBlockFilters(bf) - if len(whereTerms) > 0 { - whereClause = "WHERE " + strings.Join(whereTerms, " AND ") + "\n" - } - if len(joinTerms) > 0 { - joinClause = strings.Join(joinTerms, "\n") - } - } - - tmpl := ` - %s - SELECT bh.header - FROM %s - %s - %s - ORDER BY bh.round ASC - LIMIT %d` - - query = fmt.Sprintf(tmpl, withClause, fromTable, joinClause, whereClause, bf.Limit) - } - - return query, nil -} - -// This function blocks. `tx` must be non-nil. -func (db *IndexerDb) yieldBlocks(ctx context.Context, tx pgx.Tx, bf idb.BlockFilter, out chan<- idb.BlockRow) { - - query, err := buildBlockQuery(bf) - if err != nil { - err = fmt.Errorf("block query err %v", err) - out <- idb.BlockRow{Error: err} - return - } - - rows, err := tx.Query(ctx, query) - if err != nil { - err = fmt.Errorf("block query %#v err %v", query, err) - out <- idb.BlockRow{Error: err} - return - } - db.yieldBlocksThreadSimple(rows, out) -} - // This function blocks. `tx` must be non-nil. func (db *IndexerDb) yieldTxns(ctx context.Context, tx pgx.Tx, tf idb.TransactionFilter, out chan<- idb.TxnRow) { if len(tf.NextToken) > 0 { @@ -977,42 +770,6 @@ func txnFilterOptimization(tf idb.TransactionFilter) idb.TransactionFilter { return tf } -// Blocks is part of idb.IndexerDB -func (db *IndexerDb) Blocks(ctx context.Context, bf idb.BlockFilter) (<-chan idb.BlockRow, uint64) { - out := make(chan idb.BlockRow, 1) - - tx, err := db.db.BeginTx(ctx, readonlyRepeatableRead) - if err != nil { - out <- idb.BlockRow{Error: err} - close(out) - return out, 0 - } - - round, err := db.getMaxRoundAccounted(ctx, tx) - if err != nil { - out <- idb.BlockRow{Error: err} - close(out) - if rerr := tx.Rollback(ctx); rerr != nil { - db.log.Printf("rollback error: %s", rerr) - } - return out, round - } - - go func() { - db.yieldBlocks(ctx, tx, bf, out) - // Because we return a channel into a "callWithTimeout" function, - // We need to make sure that rollback is called before close() - // otherwise we can end up with a situation where "callWithTimeout" - // will cancel our context, resulting in connection pool churn - if rerr := tx.Rollback(ctx); rerr != nil { - db.log.Printf("rollback error: %s", rerr) - } - close(out) - }() - - return out, round -} - // Transactions is part of idb.IndexerDB func (db *IndexerDb) Transactions(ctx context.Context, tf idb.TransactionFilter) (<-chan idb.TxnRow, uint64) { out := make(chan idb.TxnRow, 1) @@ -1229,6 +986,250 @@ finish: } } +// buildBlockFilters generates filters based on a block's round and/or timestamp. +// +// Filters related to participation are generated elsewhere. +// +// Some of the filters are meant to be used as boolean conditions in the WHERE clause. +// Others, for performance reasons, have to be used as INNER JOIN terms in the FROM clause. +func buildBlockHeaderFilters(bf idb.BlockHeaderFilter) (whereTerms []string, joinTerms []string) { + + // Round-based filters + if bf.MaxRound != nil { + whereTerms = append( + whereTerms, + fmt.Sprintf("bh.round <= %d", *bf.MaxRound), + ) + } + if bf.MinRound != nil { + whereTerms = append( + whereTerms, + fmt.Sprintf("bh.round >= %d", *bf.MinRound), + ) + } + + // Timestamp-based filters + // + // Converting the timestamp into a round usually results in faster execution plans + // (compared to the execution plans that would result from using the `block_header.realtime` column directly) + // + // Unfortunately, writing this condition in the WHERE clause results in poor execution plans. + // Expressing the filter as an INNER JOIN results in an efficient query execution plan. + if !bf.AfterTime.IsZero() { + tmpl := ` + INNER JOIN ( + SELECT COALESCE(tmp.round, 0) AS round + FROM block_header tmp + WHERE tmp.realtime > (to_timestamp(%d) AT TIME ZONE 'UTC') + ORDER BY tmp.realtime ASC, tmp.round ASC + LIMIT 1 + ) bh_at ON bh.round >= bh_at.round + ` + joinTerms = append( + joinTerms, + fmt.Sprintf(tmpl, bf.AfterTime.UTC().Unix()), + ) + } + if !bf.BeforeTime.IsZero() { + tmpl := ` + INNER JOIN ( + SELECT COALESCE(tmp.round, 0) AS round + FROM block_header tmp + WHERE tmp.realtime < (to_timestamp(%d) AT TIME ZONE 'UTC') + ORDER BY tmp.realtime DESC, tmp.round DESC + LIMIT 1 + ) bh_bt ON bh.round <= bh_bt.round + ` + joinTerms = append( + joinTerms, + fmt.Sprintf(tmpl, bf.BeforeTime.UTC().Unix()), + ) + } + + return whereTerms, joinTerms +} + +func buildBlockHeadersQuery(bf idb.BlockHeaderFilter) (query string, err error) { + + // helper function to build CTEs + buildCte := func(cteName string, whereTerms []string, joinTerms []string) string { + tmpl := `%s AS ( + SELECT bh.round, bh.header + FROM block_header bh + %s + WHERE %s + ORDER BY bh.round ASC + LIMIT %d + )` + return fmt.Sprintf(tmpl, cteName, strings.Join(joinTerms, "\n"), strings.Join(whereTerms, " AND "), bf.Limit) + } + + // Build auxiliary CTEs for participation-related parameters. + // + // Using CTEs in this way turned out to be necessary to lead CockroachDB's query optimizer + // into using the execution plan we want. + // + // If we were to put the CTE filters in the main query's WHERE clause, that would result + // in a sub-optimal execution plan. At least this was the case at the time of writing. + var CTEs []string + var CteNames []string + { + if len(bf.Proposers) > 0 { + whereTerms, joinTerms := buildBlockHeaderFilters(bf) + var proposersStr []string + for addr := range bf.Proposers { + proposersStr = append(proposersStr, `'"`+addr.String()+`"'`) + } + whereTerms = append( + whereTerms, + fmt.Sprintf("( (bh.header->'prp') IS NOT NULL AND ((bh.header->'prp')::TEXT IN (%s)) )", strings.Join(proposersStr, ",")), + ) + + cteName := "prp" + cte := buildCte(cteName, whereTerms, joinTerms) + CTEs = append(CTEs, cte) + CteNames = append(CteNames, cteName) + + } + if len(bf.ExpiredParticipationAccounts) > 0 { + whereTerms, joinTerms := buildBlockHeaderFilters(bf) + var expiredStr []string + for addr := range bf.ExpiredParticipationAccounts { + expiredStr = append(expiredStr, `'`+addr.String()+`'`) + } + whereTerms = append( + whereTerms, + fmt.Sprintf("( (bh.header->'partupdrmv') IS NOT NULL AND (bh.header->'partupdrmv') ?| array[%s] )", strings.Join(expiredStr, ",")), + ) + + cteName := "expired" + CTE := buildCte(cteName, whereTerms, joinTerms) + CTEs = append(CTEs, CTE) + CteNames = append(CteNames, "expired") + + } + if len(bf.AbsentParticipationAccounts) > 0 { + whereTerms, joinTerms := buildBlockHeaderFilters(bf) + var absentStr []string + for addr := range bf.AbsentParticipationAccounts { + absentStr = append(absentStr, `'`+addr.String()+`'`) + } + whereTerms = append( + whereTerms, + fmt.Sprintf("( (bh.header->'partupdabs') IS NOT NULL AND (bh.header->'partupdabs') ?| array[%s] )", strings.Join(absentStr, ",")), + ) + + cteName := "absent" + CTE := buildCte(cteName, whereTerms, joinTerms) + CTEs = append(CTEs, CTE) + CteNames = append(CteNames, cteName) + } + if len(CteNames) > 0 { + var selects []string + for _, cteName := range CteNames { + selects = append(selects, fmt.Sprintf("SELECT * FROM %s", cteName)) + } + CTE := "tmp AS (" + strings.Join(selects, " UNION ") + ")" + CTEs = append(CTEs, CTE) + } + } + + // Build the main query. It uses the CTEs, if any. + { + var withClause string + if len(CTEs) > 0 { + withClause = "WITH " + strings.Join(CTEs, ",\n") + } + + var fromTable string + if len(CTEs) > 0 { + fromTable = "tmp bh" + } else { + fromTable = "block_header bh" + } + + var whereClause string + var joinClause string + if len(CTEs) == 0 { + whereTerms, joinTerms := buildBlockHeaderFilters(bf) + if len(whereTerms) > 0 { + whereClause = "WHERE " + strings.Join(whereTerms, " AND ") + "\n" + } + if len(joinTerms) > 0 { + joinClause = strings.Join(joinTerms, "\n") + } + } + + tmpl := ` + %s + SELECT bh.header + FROM %s + %s + %s + ORDER BY bh.round ASC + LIMIT %d` + + query = fmt.Sprintf(tmpl, withClause, fromTable, joinClause, whereClause, bf.Limit) + } + + return query, nil +} + +// This function blocks. `tx` must be non-nil. +func (db *IndexerDb) yieldBlockHeaders(ctx context.Context, tx pgx.Tx, bf idb.BlockHeaderFilter, out chan<- idb.BlockRow) { + + query, err := buildBlockHeadersQuery(bf) + if err != nil { + err = fmt.Errorf("block query err %v", err) + out <- idb.BlockRow{Error: err} + return + } + + rows, err := tx.Query(ctx, query) + if err != nil { + err = fmt.Errorf("block query %#v err %v", query, err) + out <- idb.BlockRow{Error: err} + return + } + db.yieldBlocksThreadSimple(rows, out) +} + +// BlockHeaders is part of idb.IndexerDB +func (db *IndexerDb) BlockHeaders(ctx context.Context, bf idb.BlockHeaderFilter) (<-chan idb.BlockRow, uint64) { + out := make(chan idb.BlockRow, 1) + + tx, err := db.db.BeginTx(ctx, readonlyRepeatableRead) + if err != nil { + out <- idb.BlockRow{Error: err} + close(out) + return out, 0 + } + + round, err := db.getMaxRoundAccounted(ctx, tx) + if err != nil { + out <- idb.BlockRow{Error: err} + close(out) + if rerr := tx.Rollback(ctx); rerr != nil { + db.log.Printf("rollback error: %s", rerr) + } + return out, round + } + + go func() { + db.yieldBlockHeaders(ctx, tx, bf, out) + // Because we return a channel into a "callWithTimeout" function, + // We need to make sure that rollback is called before close() + // otherwise we can end up with a situation where "callWithTimeout" + // will cancel our context, resulting in connection pool churn + if rerr := tx.Rollback(ctx); rerr != nil { + db.log.Printf("rollback error: %s", rerr) + } + close(out) + }() + + return out, round +} + var statusStrings = []string{"Offline", "Online", "NotParticipating"} const offlineStatusIdx = 0 diff --git a/idb/postgres/postgres_boxes_test.go b/idb/postgres/postgres_boxes_test.go index 3f77d18d9..dfa51ec59 100644 --- a/idb/postgres/postgres_boxes_test.go +++ b/idb/postgres/postgres_boxes_test.go @@ -11,7 +11,6 @@ import ( "github.com/stretchr/testify/require" "github.com/algorand/avm-abi/apps" - sdk "github.com/algorand/go-algorand-sdk/v2/types" "github.com/algorand/indexer/v3/idb" "github.com/algorand/indexer/v3/idb/postgres/internal/encoding" "github.com/algorand/indexer/v3/idb/postgres/internal/writer" @@ -19,6 +18,7 @@ import ( "github.com/algorand/go-algorand-sdk/v2/protocol" "github.com/algorand/go-algorand-sdk/v2/protocol/config" + sdk "github.com/algorand/go-algorand-sdk/v2/types" ) type boxTestComparator func(t *testing.T, db *IndexerDb, appBoxes map[sdk.AppIndex]map[string]string, diff --git a/idb/postgres/postgres_integration_common_test.go b/idb/postgres/postgres_integration_common_test.go index 9392cd9ef..4c17c446e 100644 --- a/idb/postgres/postgres_integration_common_test.go +++ b/idb/postgres/postgres_integration_common_test.go @@ -4,14 +4,15 @@ import ( "context" "testing" - sdk "github.com/algorand/go-algorand-sdk/v2/types" - "github.com/algorand/indexer/v3/types" "github.com/jackc/pgx/v4/pgxpool" "github.com/stretchr/testify/require" "github.com/algorand/indexer/v3/idb" pgtest "github.com/algorand/indexer/v3/idb/postgres/internal/testing" + "github.com/algorand/indexer/v3/types" "github.com/algorand/indexer/v3/util/test" + + sdk "github.com/algorand/go-algorand-sdk/v2/types" ) func setupIdbWithConnectionString(t *testing.T, connStr string, genesis sdk.Genesis) *IndexerDb { @@ -37,7 +38,7 @@ func setupIdb(t *testing.T, genesis sdk.Genesis) (*IndexerDb, func()) { Block: test.MakeGenesisBlock(), Delta: sdk.LedgerStateDelta{}, } - db.AddBlock(&vb) + require.NoError(t, db.AddBlock(&vb)) return db, newShutdownFunc } diff --git a/idb/postgres/postgres_integration_test.go b/idb/postgres/postgres_integration_test.go index ac3414407..b2c991d86 100644 --- a/idb/postgres/postgres_integration_test.go +++ b/idb/postgres/postgres_integration_test.go @@ -11,27 +11,27 @@ import ( "testing" "time" - "github.com/algorand/indexer/v3/types" "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - crypto2 "github.com/algorand/go-algorand-sdk/v2/crypto" - "github.com/algorand/go-algorand-sdk/v2/encoding/msgpack" - sdk "github.com/algorand/go-algorand-sdk/v2/types" + "github.com/algorand/go-codec/codec" "github.com/algorand/indexer/v3/api/generated/v2" "github.com/algorand/indexer/v3/idb" "github.com/algorand/indexer/v3/idb/postgres/internal/encoding" "github.com/algorand/indexer/v3/idb/postgres/internal/schema" pgtest "github.com/algorand/indexer/v3/idb/postgres/internal/testing" pgutil "github.com/algorand/indexer/v3/idb/postgres/internal/util" + "github.com/algorand/indexer/v3/types" "github.com/algorand/indexer/v3/util" "github.com/algorand/indexer/v3/util/test" + crypto2 "github.com/algorand/go-algorand-sdk/v2/crypto" "github.com/algorand/go-algorand-sdk/v2/encoding/json" + "github.com/algorand/go-algorand-sdk/v2/encoding/msgpack" "github.com/algorand/go-algorand-sdk/v2/protocol" - "github.com/algorand/go-codec/codec" + sdk "github.com/algorand/go-algorand-sdk/v2/types" ) // TestMaxRoundOnUninitializedDB makes sure we return 0 when getting the max round on a new DB. @@ -1334,7 +1334,7 @@ func TestAddBlockIncrementsMaxRoundAccounted(t *testing.T) { Block: block, Delta: sdk.LedgerStateDelta{}, } - db.AddBlock(&vb) + require.NoError(t, db.AddBlock(&vb)) round, err = db.GetNextRoundToAccount() require.NoError(t, err) @@ -1347,7 +1347,7 @@ func TestAddBlockIncrementsMaxRoundAccounted(t *testing.T) { Block: block, Delta: sdk.LedgerStateDelta{}, } - db.AddBlock(&vb) + require.NoError(t, db.AddBlock(&vb)) require.NoError(t, err) round, err = db.GetNextRoundToAccount() @@ -1361,7 +1361,7 @@ func TestAddBlockIncrementsMaxRoundAccounted(t *testing.T) { Block: block, Delta: sdk.LedgerStateDelta{}, } - db.AddBlock(&vb) + require.NoError(t, db.AddBlock(&vb)) round, err = db.GetNextRoundToAccount() require.NoError(t, err) @@ -1626,7 +1626,7 @@ func TestSearchForInnerTransactionReturnsRootTransaction(t *testing.T) { Block: test.MakeGenesisBlock(), Delta: sdk.LedgerStateDelta{}, } - db.AddBlock(&genblk) + require.NoError(t, db.AddBlock(&genblk)) return db.AddBlock(&vb) }, nil) require.NoError(t, err) @@ -2186,11 +2186,6 @@ func TestDeleteTransactions(t *testing.T) { txns := []sdk.SignedTxn{} - genBlock := types.ValidatedBlock{ - Block: test.MakeGenesisBlock(), - Delta: sdk.LedgerStateDelta{}, - } - db.AddBlock(&genBlock) // add 4 rounds of txns // txnA := test.MakeCreateAppTxn(test.AccountA) // txnB := test.MakeCreateAppTxn(test.AccountB) diff --git a/idb/postgres/postgres_migrations_test.go b/idb/postgres/postgres_migrations_test.go index 6837ceacb..afb4c22c9 100644 --- a/idb/postgres/postgres_migrations_test.go +++ b/idb/postgres/postgres_migrations_test.go @@ -7,9 +7,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - sdk "github.com/algorand/go-algorand-sdk/v2/types" pgtest "github.com/algorand/indexer/v3/idb/postgres/internal/testing" "github.com/algorand/indexer/v3/idb/postgres/internal/types" + + sdk "github.com/algorand/go-algorand-sdk/v2/types" ) func TestConvertAccountDataIncrementsMigrationNumber(t *testing.T) { diff --git a/idb/postgres/postgres_rand_test.go b/idb/postgres/postgres_rand_test.go index bcf433ac7..c6bcdae9a 100644 --- a/idb/postgres/postgres_rand_test.go +++ b/idb/postgres/postgres_rand_test.go @@ -11,12 +11,12 @@ import ( "github.com/jackc/pgx/v4" "github.com/stretchr/testify/require" - sdk "github.com/algorand/go-algorand-sdk/v2/types" - models "github.com/algorand/indexer/v3/api/generated/v2" "github.com/algorand/indexer/v3/idb" "github.com/algorand/indexer/v3/idb/postgres/internal/writer" "github.com/algorand/indexer/v3/util/test" + + sdk "github.com/algorand/go-algorand-sdk/v2/types" ) func generateAddress(t *testing.T) sdk.Address { diff --git a/idb/postgres/postgres_test.go b/idb/postgres/postgres_test.go index c797b1b55..d034a02d3 100644 --- a/idb/postgres/postgres_test.go +++ b/idb/postgres/postgres_test.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - sdk "github.com/algorand/go-algorand-sdk/v2/types" - "github.com/algorand/indexer/v3/idb" "github.com/algorand/indexer/v3/types" + + sdk "github.com/algorand/go-algorand-sdk/v2/types" ) func Test_txnFilterOptimization(t *testing.T) { @@ -76,11 +76,11 @@ func Test_UnknownProtocol(t *testing.T) { } func Test_buildTransactionQueryTime(t *testing.T) { - us_east_tz, err := time.LoadLocation("America/New_York") + usEastTZ, err := time.LoadLocation("America/New_York") require.NoError(t, err) - us_west_tz, err := time.LoadLocation("America/Los_Angeles") + usWestTZ, err := time.LoadLocation("America/Los_Angeles") require.NoError(t, err) - random_date_utc := time.Date(1000, time.December, 25, 1, 2, 3, 4, time.UTC) + randomDateUTC := time.Date(1000, time.December, 25, 1, 2, 3, 4, time.UTC) tests := []struct { name string arg idb.TransactionFilter @@ -89,26 +89,26 @@ func Test_buildTransactionQueryTime(t *testing.T) { { "BeforeTime UTC to UTC", idb.TransactionFilter{ - BeforeTime: random_date_utc, + BeforeTime: randomDateUTC, }, - []interface{}{random_date_utc}, + []interface{}{randomDateUTC}, }, { "AfterTime UTC to UTC", idb.TransactionFilter{ - AfterTime: random_date_utc, + AfterTime: randomDateUTC, }, - []interface{}{random_date_utc}, + []interface{}{randomDateUTC}, }, { "BeforeTime AfterTime Conversion", idb.TransactionFilter{ - BeforeTime: time.Date(1000, time.December, 25, 1, 2, 3, 4, us_east_tz), - AfterTime: time.Date(1000, time.December, 25, 1, 2, 3, 4, us_west_tz), + BeforeTime: time.Date(1000, time.December, 25, 1, 2, 3, 4, usEastTZ), + AfterTime: time.Date(1000, time.December, 25, 1, 2, 3, 4, usWestTZ), }, []interface{}{ - time.Date(1000, time.December, 25, 1, 2, 3, 4, us_east_tz).UTC(), - time.Date(1000, time.December, 25, 1, 2, 3, 4, us_west_tz).UTC(), + time.Date(1000, time.December, 25, 1, 2, 3, 4, usEastTZ).UTC(), + time.Date(1000, time.December, 25, 1, 2, 3, 4, usWestTZ).UTC(), }, }, } diff --git a/idb/txn_type_enum.go b/idb/txn_type_enum.go index 39f0c6cac..286129dce 100644 --- a/idb/txn_type_enum.go +++ b/idb/txn_type_enum.go @@ -20,6 +20,7 @@ const ( TypeEnumAssetFreeze TypeEnumApplication TypeEnumStateProof + TypeEnumHeartbeat ) var typeEnumMap = map[string]TxnTypeEnum{ @@ -30,6 +31,7 @@ var typeEnumMap = map[string]TxnTypeEnum{ "afrz": TypeEnumAssetFreeze, "appl": TypeEnumApplication, "stpf": TypeEnumStateProof, + "hb": TypeEnumHeartbeat, } func makeTypeEnumString() string { diff --git a/misc/requirements.txt b/misc/requirements.txt index 611315496..1551f18cb 100644 --- a/misc/requirements.txt +++ b/misc/requirements.txt @@ -4,5 +4,5 @@ msgpack >=1,<2 py-algorand-sdk >=1.3.0,<2 pytest==6.2.5 GitPython==3.1.41 -PyYAML==6.0 -requests==2.32.0 \ No newline at end of file +PyYAML==6.0.2 +requests==2.32.0 diff --git a/monitoring/Dockerfile-indexer b/monitoring/Dockerfile-indexer index d2cf3597a..17fdea1cc 100644 --- a/monitoring/Dockerfile-indexer +++ b/monitoring/Dockerfile-indexer @@ -1,4 +1,4 @@ -ARG GO_VERSION=1.21.10 +ARG GO_VERSION=1.23.3 FROM golang:$GO_VERSION ENV DEBIAN_FRONTEND noninteractive diff --git a/monitoring/docker-compose.yml b/monitoring/docker-compose.yml index afc8431fb..0cbdd5b3d 100644 --- a/monitoring/docker-compose.yml +++ b/monitoring/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3' services: indexer: container_name: "indexer" diff --git a/types/min_balance.go b/types/min_balance.go index be9f99411..22813e96f 100644 --- a/types/min_balance.go +++ b/types/min_balance.go @@ -32,41 +32,41 @@ func minBalance( totalExtraAppPages uint64, totalBoxes uint64, totalBoxBytes uint64, ) uint64 { - var min uint64 + var minBal uint64 // First, base MinBalance - min = proto.MinBalance + minBal = proto.MinBalance // MinBalance for each Asset assetCost := proto.MinBalance * totalAssets - min += assetCost + minBal += assetCost // Base MinBalance for each created application appCreationCost := proto.AppFlatParamsMinBalance * totalAppParams - min += appCreationCost + minBal += appCreationCost // Base MinBalance for each opted in application appOptInCost := proto.AppFlatOptInMinBalance * totalAppLocalStates - min += appOptInCost + minBal += appOptInCost // MinBalance for state usage measured by LocalStateSchemas and // GlobalStateSchemas schemaCost := stateSchemaMinBalance(totalAppSchema, proto) - min += schemaCost + minBal += schemaCost // MinBalance for each extra app program page extraAppProgramLenCost := proto.AppFlatParamsMinBalance * totalExtraAppPages - min += extraAppProgramLenCost + minBal += extraAppProgramLenCost // Base MinBalance for each created box boxBaseCost := proto.BoxFlatMinBalance * totalBoxes - min += boxBaseCost + minBal += boxBaseCost // Per byte MinBalance for boxes boxByteCost := proto.BoxByteMinBalance * totalBoxBytes - min += boxByteCost + minBal += boxByteCost - return min + return minBal } // AccountMinBalance computes the minimum balance requirements for an account diff --git a/types/min_balance_test.go b/types/min_balance_test.go index 485deae5f..8f97d028e 100644 --- a/types/min_balance_test.go +++ b/types/min_balance_test.go @@ -1,10 +1,12 @@ package types import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/algorand/go-algorand-sdk/v2/protocol/config" sdk "github.com/algorand/go-algorand-sdk/v2/types" - "github.com/stretchr/testify/assert" - "testing" ) func TestMinBalance(t *testing.T) { diff --git a/util/test/account_testutil.go b/util/test/account_testutil.go index 876d6e1cc..a5e475e69 100644 --- a/util/test/account_testutil.go +++ b/util/test/account_testutil.go @@ -100,7 +100,7 @@ func MakeAssetConfigTxn(configid, total, decimals uint64, defaultFrozen bool, un } // MakeAssetTransferTxn creates an asset transfer transaction. -func MakeAssetTransferTxn(assetid, amt uint64, sender, receiver, close sdk.Address) sdk.SignedTxnWithAD { +func MakeAssetTransferTxn(assetid, amt uint64, sender, receiver, closeTo sdk.Address) sdk.SignedTxnWithAD { return sdk.SignedTxnWithAD{ SignedTxn: sdk.SignedTxn{ Txn: sdk.Transaction{ @@ -117,7 +117,7 @@ func MakeAssetTransferTxn(assetid, amt uint64, sender, receiver, close sdk.Addre //only used for clawback transactions //AssetSender: sdk.Address{}, AssetReceiver: receiver, - AssetCloseTo: close, + AssetCloseTo: closeTo, }, }, Sig: Signature, @@ -127,7 +127,7 @@ func MakeAssetTransferTxn(assetid, amt uint64, sender, receiver, close sdk.Addre // MakePaymentTxn creates an algo transfer transaction. func MakePaymentTxn(fee, amt, closeAmt, sendRewards, receiveRewards, - closeRewards uint64, sender, receiver, close, rekeyTo sdk.Address) sdk.SignedTxnWithAD { + closeRewards uint64, sender, receiver, closeTo, rekeyTo sdk.Address) sdk.SignedTxnWithAD { return sdk.SignedTxnWithAD{ SignedTxn: sdk.SignedTxn{ Txn: sdk.Transaction{ @@ -143,7 +143,7 @@ func MakePaymentTxn(fee, amt, closeAmt, sendRewards, receiveRewards, PaymentTxnFields: sdk.PaymentTxnFields{ Receiver: receiver, Amount: sdk.MicroAlgos(amt), - CloseRemainderTo: close, + CloseRemainderTo: closeTo, }, }, Sig: Signature, diff --git a/util/util_test.go b/util/util_test.go index d20997d19..5a4bbd271 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -101,7 +101,7 @@ func TestDecodeSignedTxnErrors(t *testing.T) { // v16.RequireGenesisHash = true b.BlockHeader.CurrentProtocol = "https://github.com/algorand/spec/tree/22726c9dcd12d9cddce4a8bd7e8ccaa707f74101" txib.HasGenesisHash = true - _, ad, err := DecodeSignedTxn(b.BlockHeader, txib) + _, _, err = DecodeSignedTxn(b.BlockHeader, txib) require.Contains(t, err.Error(), "HasGenesisHash set to true but RequireGenesisHash obviates the flag") // gh not empty @@ -112,7 +112,7 @@ func TestDecodeSignedTxnErrors(t *testing.T) { // if !proto.SupportSignedTxnInBlock, applyData is empty b.BlockHeader.CurrentProtocol = "v10" - _, ad, err = DecodeSignedTxn(b.BlockHeader, txib) + _, ad, err := DecodeSignedTxn(b.BlockHeader, txib) require.Equal(t, sdk.ApplyData{}, ad) // genesisID not empty