diff --git a/accounting/rewind.go b/accounting/rewind.go deleted file mode 100644 index cfc6cdcf7..000000000 --- a/accounting/rewind.go +++ /dev/null @@ -1,188 +0,0 @@ -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/accounting/rewind_test.go b/accounting/rewind_test.go deleted file mode 100644 index caa77a960..000000000 --- a/accounting/rewind_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package accounting - -import ( - "context" - "errors" - "testing" - - sdk "github.com/algorand/go-algorand-sdk/v2/types" - "github.com/algorand/indexer/v3/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - - models "github.com/algorand/indexer/v3/api/generated/v2" - "github.com/algorand/indexer/v3/idb" - "github.com/algorand/indexer/v3/idb/mocks" -) - -func TestBasic(t *testing.T) { - var a sdk.Address - a[0] = 'a' - - account := models.Account{ - Address: a.String(), - Amount: 100, - AmountWithoutPendingRewards: 100, - Round: 8, - } - - txnRow := idb.TxnRow{ - Round: 7, - Txn: &sdk.SignedTxnWithAD{ - SignedTxn: sdk.SignedTxn{ - Txn: sdk.Transaction{ - Type: sdk.PaymentTx, - PaymentTxnFields: sdk.PaymentTxnFields{ - Receiver: a, - Amount: sdk.MicroAlgos(2), - }, - }, - }, - }, - } - - ch := make(chan idb.TxnRow, 1) - ch <- txnRow - close(ch) - var outCh <-chan idb.TxnRow = ch - - db := &mocks.IndexerDb{} - db.On("GetSpecialAccounts", mock.Anything).Return(types.SpecialAddresses{}, nil) - db.On("Transactions", mock.Anything, mock.Anything).Return(outCh, uint64(8)) - - account, err := AccountAtRound(context.Background(), account, 6, db) - assert.NoError(t, err) - - assert.Equal(t, uint64(98), account.Amount) -} - -// Test that when idb.Transactions() returns stale data the first time, we return an error. -func TestStaleTransactions1(t *testing.T) { - var a sdk.Address - a[0] = 'a' - - account := models.Account{ - Address: a.String(), - Round: 8, - } - - ch := make(chan idb.TxnRow) - var outCh <-chan idb.TxnRow = ch - close(ch) - - db := &mocks.IndexerDb{} - db.On("GetSpecialAccounts", mock.Anything).Return(types.SpecialAddresses{}, nil) - db.On("Transactions", mock.Anything, mock.Anything).Return(outCh, uint64(7)).Once() - - account, err := AccountAtRound(context.Background(), account, 6, db) - assert.True(t, errors.As(err, &ConsistencyError{}), "err: %v", err) -} diff --git a/api/error_messages.go b/api/error_messages.go index d9c339ada..a1bfbfbc2 100644 --- a/api/error_messages.go +++ b/api/error_messages.go @@ -36,8 +36,7 @@ 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" - errMultiAcctRewind = "multiple accounts rewind is not supported by this server" - errRewindingAccount = "error while rewinding account" + errRewindingAccountNotSupported = "rewinding account is no longer supported, please remove the `round=` query parameter and try again" errLookingUpBlockForRound = "error while looking up block for round" errTransactionSearch = "error while searching for transaction" errZeroAddressCloseRemainderToRole = "searching transactions by zero address with close address role is not supported" diff --git a/api/generated/v2/routes.go b/api/generated/v2/routes.go index 14159f619..6b9f38d0d 100644 --- a/api/generated/v2/routes.go +++ b/api/generated/v2/routes.go @@ -1212,230 +1212,229 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "/l8AAAD//84PGZ5wfgEA", + "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", + "lrsEplHBUCfD7ulxPOC1avkiYIHke/RC2D+7HX2PFvGo89WCm6H57LfJ/UcLnsK/TuFfJ9H0Pr0H4JDP", + "//DXc7/HAFzzMUnObcPx0mRcsfzkK3CnvgJA5sbSwntMKg1TnsjNSZn3abs6dCnm+YyWVORsr0YOWW9t", + "QA3t6/aslxIIisuHDwRmJ0X1k51ko5NsdCpddwpsGhvYdDSm67jcSEw8R0lpr7ngp2ydqVdv1jwNJ5Ht", + "z8SAHJLqomWeAF2so0+78l1glgv7pGLmi50y3ynbxSnbxSnbxSnbxSnbxcexRp/yUpzyUpzEt3/vvBRj", + "PE6cEdMCKgVDV+ZWY3z+B7mQu3ZC6S3qmVzNuGCNAORX0BQLNdIeFDRaUhPeYd/QSKKDl8GedWVKlgPv", + "KzjhgFCcM34N/50rxn5nmaHKMtdj3tvWajyAUBozmj+ujXnQ2ixTjAo34vOBaFdGVa0gDa0JuWoJJX4l", + "U8snb2VN1nBZSn4F/V1dTbvpKyjI2qnRaiQxqh40TrvuGcCzN/PI9D4MQKckKqckKqckKn8CbcislPmV", + "Pv8DjjpDPcJeIzZ0GlJiPLUf9yku8DLidOm0UDFAtyRqPzBaMEWkffTnJV2ckX/Yywm3D1xLjafQ00Zn", + "A2skhWSoC3EKgC4PoAfo3xKmzOyUd0sCd0arwEmcAsM/4+s5SjUZeYaOzcDb1Uh6dj3NNnIN7HiXaQ9i", + "4mG5fYOX6knTedJ0njSdJ03nSdN5yut70p+e9Kcn/elJf3rSn570p3euP/2YOs+7rxV60qqetKontc1H", + "DQuKj/b8DysT7Q8MIlZ8LFsv5JCKNca6MdFBTii7vxxq90hCou066LKOv5ynGJoTeflUtMIfphPN1LW/", + "67UqJxeTpTGVvjg/Zxu6qkp2lsvVOSSpcP3/CHy/XK3goQq/uJGjXxwps903mVTcvr1lptd0sWAqszMj", + "zI/PHk0+/J8AAAD//3BbS4jifQEA", } // 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 eeef46d88..36d8da56d 100644 --- a/api/generated/v2/types.go +++ b/api/generated/v2/types.go @@ -1499,7 +1499,7 @@ type SearchForAccountsParams struct { // AuthAddr Include accounts configured to use this spending key. AuthAddr *string `form:"auth-addr,omitempty" json:"auth-addr,omitempty"` - // 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 Deprecated and disallowed. This parameter used to include results for a specified round. Requests with this parameter set are now rejected. Round *uint64 `form:"round,omitempty" json:"round,omitempty"` // ApplicationId Application ID @@ -1511,7 +1511,7 @@ type SearchForAccountsParamsExclude string // LookupAccountByIDParams defines parameters for LookupAccountByID. type LookupAccountByIDParams struct { - // Round Include results for the specified round. + // Round Deprecated and disallowed. This parameter used to include results for a specified round. Requests with this parameter set are now rejected. 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. diff --git a/api/handlers.go b/api/handlers.go index ca1333535..78f15ef7c 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -17,7 +17,6 @@ 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" @@ -29,13 +28,6 @@ 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 @@ -196,8 +188,9 @@ func (si *ServerImplementation) LookupAccountByID(ctx echo.Context, accountID st if err := si.verifyHandler("LookupAccountByID", ctx); err != nil { return badRequest(ctx, err.Error()) } - if 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) } addr, decodeErrors := decodeAddress(&accountID, "account-id", make([]string, 0)) @@ -227,7 +220,7 @@ func (si *ServerImplementation) LookupAccountByID(ctx echo.Context, accountID st } } - accounts, round, err := si.fetchAccounts(ctx.Request().Context(), options, params.Round) + accounts, round, err := si.fetchAccounts(ctx.Request().Context(), options) if err != nil { var maxErr idb.MaxAPIResourcesPerAccountError if errors.As(err, &maxErr) { @@ -385,13 +378,13 @@ 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.Round != nil && uint64(*params.Round) > math.MaxInt64) { + (params.ApplicationId != nil && uint64(*params.ApplicationId) > math.MaxInt64) { return notFound(ctx, errValueExceedingInt64) } - if !si.EnableAddressSearchRoundRewind && params.Round != nil { - return badRequest(ctx, errMultiAcctRewind) + // The Round parameter is no longer supported (as it was used to request account rewinding) + if params.Round != nil { + return badRequest(ctx, errRewindingAccountNotSupported) } spendingAddr, decodeErrors := decodeAddress(params.AuthAddr, "account-id", make([]string, 0)) @@ -440,7 +433,7 @@ func (si *ServerImplementation) SearchForAccounts(ctx echo.Context, params gener options.GreaterThanAddress = addr[:] } - accounts, round, err := si.fetchAccounts(ctx.Request().Context(), options, params.Round) + accounts, round, err := si.fetchAccounts(ctx.Request().Context(), options) if err != nil { var maxErr idb.MaxAPIResourcesPerAccountError if errors.As(err, &maxErr) { @@ -1391,7 +1384,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, atRound *uint64) ([]generated.Account, uint64 /*round*/, error) { +func (si *ServerImplementation) fetchAccounts(ctx context.Context, options idb.AccountQueryOptions) ([]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 { @@ -1404,33 +1397,12 @@ 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 } - // 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 - } + account := row.Account // match the algod equivalent which includes pending rewards account.Rewards += account.PendingRewards diff --git a/api/handlers_test.go b/api/handlers_test.go index 18c152698..c5b2e40da 100644 --- a/api/handlers_test.go +++ b/api/handlers_test.go @@ -9,7 +9,6 @@ import ( "math" "net/http" "net/http/httptest" - "strings" "testing" "time" @@ -664,7 +663,6 @@ func TestFetchTransactions(t *testing.T) { // Setup the mocked responses mockIndexer := &mocks.IndexerDb{} si := testServerImplementation(mockIndexer) - si.EnableAddressSearchRoundRewind = true si.timeout = 1 * time.Second roundTime := time.Now() @@ -740,26 +738,9 @@ func TestFetchTransactions(t *testing.T) { } } -func TestFetchAccountsRewindRoundTooLarge(t *testing.T) { - ch := make(chan idb.AccountRow) - close(ch) - var outCh <-chan idb.AccountRow = ch - - db := &mocks.IndexerDb{} - db.On("GetAccounts", mock.Anything, mock.Anything).Return(outCh, uint64(7)).Once() - - si := testServerImplementation(db) - si.EnableAddressSearchRoundRewind = true - atRound := uint64(8) - _, _, err := si.fetchAccounts(context.Background(), idb.AccountQueryOptions{}, &atRound) - assert.Error(t, err) - assert.True(t, strings.HasPrefix(err.Error(), errRewindingAccount), err.Error()) -} - func TestLookupApplicationLogsByID(t *testing.T) { mockIndexer := &mocks.IndexerDb{} si := testServerImplementation(mockIndexer) - si.EnableAddressSearchRoundRewind = true txnBytes := loadResourceFileOrPanic("test_resources/app_call_logs.txn") var stxn sdk.SignedTxnWithAD @@ -1119,13 +1100,6 @@ func TestBigNumbers(t *testing.T) { return si.SearchForApplications(ctx, generated.SearchForApplicationsParams{ApplicationId: uint64Ptr(uint64(math.MaxInt64 + 1))}) }, }, - { - name: "SearchForAccountInvalidRound", - errString: errValueExceedingInt64, - callHandler: func(ctx echo.Context, si ServerImplementation) error { - return si.SearchForAccounts(ctx, generated.SearchForAccountsParams{Round: uint64Ptr(uint64(math.MaxInt64 + 1))}) - }, - }, { name: "SearchForAccountInvalidAppID", errString: errValueExceedingInt64, @@ -1140,15 +1114,6 @@ func TestBigNumbers(t *testing.T) { return si.SearchForAccounts(ctx, generated.SearchForAccountsParams{AssetId: uint64Ptr(uint64(math.MaxInt64 + 1))}) }, }, - { - name: "LookupAccountByID", - errString: errValueExceedingInt64, - callHandler: func(ctx echo.Context, si ServerImplementation) error { - return si.LookupAccountByID(ctx, - "PBH2JQNVP5SBXLTOWNHHPGU6FUMBVS4ZDITPK5RA5FG2YIIFS6UYEMFM2Y", - generated.LookupAccountByIDParams{Round: uint64Ptr(uint64(math.MaxInt64 + 1))}) - }, - }, { name: "SearchForAssets", errString: errValueExceedingInt64, @@ -1230,6 +1195,53 @@ 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 ae495ea16..76c11d21d 100644 --- a/api/indexer.oas2.json +++ b/api/indexer.oas2.json @@ -80,8 +80,7 @@ }, { "type": "integer", - "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.", - + "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" }, @@ -120,7 +119,10 @@ "$ref": "#/parameters/account-id" }, { - "$ref": "#/parameters/round" + "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/include-all" diff --git a/api/indexer.oas3.yml b/api/indexer.oas3.yml index 9611bb81b..9a425b354 100644 --- a/api/indexer.oas3.yml +++ b/api/indexer.oas3.yml @@ -2480,7 +2480,7 @@ "x-algorand-format": "Address" }, { - "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.", + "description": "Deprecated and disallowed. This parameter used to include results for a specified round. Requests with this parameter set are now rejected.", "in": "query", "name": "round", "schema": { @@ -2592,7 +2592,7 @@ } }, { - "description": "Include results for the specified round.", + "description": "Deprecated and disallowed. This parameter used to include results for a specified round. Requests with this parameter set are now rejected.", "in": "query", "name": "round", "schema": { diff --git a/api/server.go b/api/server.go index 94d19ba32..0f4d402d5 100644 --- a/api/server.go +++ b/api/server.go @@ -23,9 +23,6 @@ type ExtraOptions struct { // Tokens are the access tokens which can access the API. Tokens []string - // DeveloperMode turns on features like AddressSearchRoundRewind - DeveloperMode bool - // MetricsEndpoint turns on the /metrics endpoint for prometheus metrics. MetricsEndpoint bool @@ -133,13 +130,12 @@ func Serve(ctx context.Context, serveAddr string, db idb.IndexerDb, dataError fu } api := ServerImplementation{ - EnableAddressSearchRoundRewind: options.DeveloperMode, - db: db, - dataError: dataError, - timeout: options.handlerTimeout(), - log: log, - disabledParams: disabledMap, - opts: options, + db: db, + dataError: dataError, + timeout: options.handlerTimeout(), + log: log, + disabledParams: disabledMap, + opts: options, } generated.RegisterHandlers(e, &api, middleware...) diff --git a/cmd/algorand-indexer/daemon.go b/cmd/algorand-indexer/daemon.go index d724601ee..ce73ea764 100644 --- a/cmd/algorand-indexer/daemon.go +++ b/cmd/algorand-indexer/daemon.go @@ -70,7 +70,7 @@ 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, "allow performance intensive operations like searching for accounts at a particular round") + cfg.flags.BoolVarP(&cfg.developerMode, "dev-mode", "", false, "has no effect currently, reserved for future performance intensive operations") 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") @@ -300,7 +300,6 @@ func runDaemon(daemonConfig *daemonConfig) error { // makeOptions converts CLI options to server options func makeOptions(daemonConfig *daemonConfig) (options api.ExtraOptions) { - options.DeveloperMode = daemonConfig.developerMode if daemonConfig.tokenString != "" { options.Tokens = append(options.Tokens, daemonConfig.tokenString) } diff --git a/cmd/idbtest/idbtest.go b/cmd/idbtest/idbtest.go index e6a62554e..b2f6c3df1 100644 --- a/cmd/idbtest/idbtest.go +++ b/cmd/idbtest/idbtest.go @@ -9,7 +9,6 @@ import ( "os" "time" - "github.com/algorand/indexer/v3/accounting" models "github.com/algorand/indexer/v3/api/generated/v2" "github.com/algorand/indexer/v3/idb" _ "github.com/algorand/indexer/v3/idb/postgres" @@ -160,9 +159,6 @@ func main() { MaxRound: account.Round, } printTxnQuery(db, tf) - raccount, err := accounting.AccountAtRound(context.Background(), account, round, db) - maybeFail(err, "AccountAtRound, %v", err) - fmt.Printf("raccount %s\n", string(ajson.Encode(raccount))) } if txntest {