diff --git a/AGENTS.md b/AGENTS.md index 88625609f6..600e54d008 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -154,6 +154,11 @@ Ledger uses independent state machines that can rebuild from blockchain events, - Race detection enabled for concurrent code validation - Benchmark tests for performance-critical paths +### Paginated Resource Lookups (`ledger/acctupdates.go`) +The `lookupAssetResources`, `lookupApplicationResources`, and `lookupBoxResources` functions follow the same pattern: walk in-memory deltas backwards, query the DB, then merge the two result sets. These functions must stay closely aligned — a bug fix or structural change in one almost certainly requires the same change in the others. They are candidates for future consolidation into shared generic logic. + +Their corresponding `Ledger`-level wrappers in `ledger/ledger.go` (`LookupAssets`, `LookupApplications`, `LookupBoxes`) must each hold `trackerMu.RLock()`, matching every other method that accesses tracker state. + ### Code Organization - Interface-first design for testability and modularity - Dependency injection for component assembly diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index 3372b6ce19..6f566f007c 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -282,7 +282,7 @@ "/v2/accounts/{address}/assets": { "get": { "description": "Lookup an account's asset holdings.", - "tags": ["public", "experimental"], + "tags": ["public", "nonparticipating"], "produces": ["application/json"], "schemes": ["http"], "summary": "Get a list of assets held by an account, inclusive of asset params.", @@ -329,7 +329,7 @@ "/v2/accounts/{address}/applications": { "get": { "description": "Lookup an account's application holdings (local state and params if the account is the creator).", - "tags": ["public", "experimental"], + "tags": ["public", "nonparticipating"], "produces": ["application/json"], "schemes": ["http"], "summary": "Get a list of applications held by an account.", diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index 1d412e78bc..8fe08aa12c 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -3411,7 +3411,7 @@ "summary": "Get a list of applications held by an account.", "tags": [ "public", - "experimental" + "nonparticipating" ] } }, @@ -3667,7 +3667,7 @@ "summary": "Get a list of assets held by an account, inclusive of asset params.", "tags": [ "public", - "experimental" + "nonparticipating" ] } }, diff --git a/daemon/algod/api/server/v2/generated/experimental/routes.go b/daemon/algod/api/server/v2/generated/experimental/routes.go index 3af8734d9d..b197616e66 100644 --- a/daemon/algod/api/server/v2/generated/experimental/routes.go +++ b/daemon/algod/api/server/v2/generated/experimental/routes.go @@ -8,26 +8,17 @@ import ( "compress/gzip" "encoding/base64" "fmt" - "net/http" "net/url" "path" "strings" . "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" - "github.com/algorand/go-algorand/data/basics" "github.com/getkin/kin-openapi/openapi3" "github.com/labstack/echo/v4" - "github.com/oapi-codegen/runtime" ) // ServerInterface represents all server handlers. type ServerInterface interface { - // Get a list of applications held by an account. - // (GET /v2/accounts/{address}/applications) - AccountApplicationsInformation(ctx echo.Context, address basics.Address, params AccountApplicationsInformationParams) error - // Get a list of assets held by an account, inclusive of asset params. - // (GET /v2/accounts/{address}/assets) - AccountAssetsInformation(ctx echo.Context, address basics.Address, params AccountAssetsInformationParams) error // Returns OK if experimental API is enabled. // (GET /v2/experimental) ExperimentalCheck(ctx echo.Context) error @@ -41,81 +32,6 @@ type ServerInterfaceWrapper struct { Handler ServerInterface } -// AccountApplicationsInformation converts echo context to params. -func (w *ServerInterfaceWrapper) AccountApplicationsInformation(ctx echo.Context) error { - var err error - // ------------- Path parameter "address" ------------- - var address basics.Address - - err = runtime.BindStyledParameterWithOptions("simple", "address", ctx.Param("address"), &address, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter address: %s", err)) - } - - ctx.Set(Api_keyScopes, []string{}) - - // Parameter object where we will unmarshal all parameters from the context - var params AccountApplicationsInformationParams - // ------------- 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 "include" ------------- - - err = runtime.BindQueryParameter("form", false, false, "include", ctx.QueryParams(), ¶ms.Include) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter include: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.AccountApplicationsInformation(ctx, address, params) - return err -} - -// AccountAssetsInformation converts echo context to params. -func (w *ServerInterfaceWrapper) AccountAssetsInformation(ctx echo.Context) error { - var err error - // ------------- Path parameter "address" ------------- - var address basics.Address - - err = runtime.BindStyledParameterWithOptions("simple", "address", ctx.Param("address"), &address, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter address: %s", err)) - } - - ctx.Set(Api_keyScopes, []string{}) - - // Parameter object where we will unmarshal all parameters from the context - var params AccountAssetsInformationParams - // ------------- 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)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.AccountAssetsInformation(ctx, address, params) - return err -} - // ExperimentalCheck converts echo context to params. func (w *ServerInterfaceWrapper) ExperimentalCheck(ctx echo.Context) error { var err error @@ -166,8 +82,6 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL Handler: si, } - router.GET(baseURL+"/v2/accounts/:address/applications", wrapper.AccountApplicationsInformation, m...) - router.GET(baseURL+"/v2/accounts/:address/assets", wrapper.AccountAssetsInformation, m...) router.GET(baseURL+"/v2/experimental", wrapper.ExperimentalCheck, m...) router.POST(baseURL+"/v2/transactions/async", wrapper.RawTransactionAsync, m...) @@ -176,241 +90,233 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "EAEA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go index 24da920747..53407e127c 100644 --- a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go +++ b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go @@ -25,9 +25,15 @@ type ServerInterface interface { // Get account information. // (GET /v2/accounts/{address}) AccountInformation(ctx echo.Context, address basics.Address, params AccountInformationParams) error + // Get a list of applications held by an account. + // (GET /v2/accounts/{address}/applications) + AccountApplicationsInformation(ctx echo.Context, address basics.Address, params AccountApplicationsInformationParams) error // Get account information about a given app. // (GET /v2/accounts/{address}/applications/{application-id}) AccountApplicationInformation(ctx echo.Context, address basics.Address, applicationId basics.AppIndex, params AccountApplicationInformationParams) error + // Get a list of assets held by an account, inclusive of asset params. + // (GET /v2/accounts/{address}/assets) + AccountAssetsInformation(ctx echo.Context, address basics.Address, params AccountAssetsInformationParams) error // Get account information about a given asset. // (GET /v2/accounts/{address}/assets/{asset-id}) AccountAssetInformation(ctx echo.Context, address basics.Address, assetId basics.AssetIndex, params AccountAssetInformationParams) error @@ -144,6 +150,47 @@ func (w *ServerInterfaceWrapper) AccountInformation(ctx echo.Context) error { return err } +// AccountApplicationsInformation converts echo context to params. +func (w *ServerInterfaceWrapper) AccountApplicationsInformation(ctx echo.Context) error { + var err error + // ------------- Path parameter "address" ------------- + var address basics.Address + + err = runtime.BindStyledParameterWithOptions("simple", "address", ctx.Param("address"), &address, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter address: %s", err)) + } + + ctx.Set(Api_keyScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params AccountApplicationsInformationParams + // ------------- 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 "include" ------------- + + err = runtime.BindQueryParameter("form", false, false, "include", ctx.QueryParams(), ¶ms.Include) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter include: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.AccountApplicationsInformation(ctx, address, params) + return err +} + // AccountApplicationInformation converts echo context to params. func (w *ServerInterfaceWrapper) AccountApplicationInformation(ctx echo.Context) error { var err error @@ -179,6 +226,40 @@ func (w *ServerInterfaceWrapper) AccountApplicationInformation(ctx echo.Context) return err } +// AccountAssetsInformation converts echo context to params. +func (w *ServerInterfaceWrapper) AccountAssetsInformation(ctx echo.Context) error { + var err error + // ------------- Path parameter "address" ------------- + var address basics.Address + + err = runtime.BindStyledParameterWithOptions("simple", "address", ctx.Param("address"), &address, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter address: %s", err)) + } + + ctx.Set(Api_keyScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params AccountAssetsInformationParams + // ------------- 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)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.AccountAssetsInformation(ctx, address, params) + return err +} + // AccountAssetInformation converts echo context to params. func (w *ServerInterfaceWrapper) AccountAssetInformation(ctx echo.Context) error { var err error @@ -722,7 +803,9 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL } router.GET(baseURL+"/v2/accounts/:address", wrapper.AccountInformation, m...) + router.GET(baseURL+"/v2/accounts/:address/applications", wrapper.AccountApplicationsInformation, m...) router.GET(baseURL+"/v2/accounts/:address/applications/:application-id", wrapper.AccountApplicationInformation, m...) + router.GET(baseURL+"/v2/accounts/:address/assets", wrapper.AccountAssetsInformation, m...) router.GET(baseURL+"/v2/accounts/:address/assets/:asset-id", wrapper.AccountAssetInformation, m...) router.GET(baseURL+"/v2/applications/:application-id", wrapper.GetApplicationByID, m...) router.GET(baseURL+"/v2/applications/:application-id/box", wrapper.GetApplicationBoxByName, m...) @@ -754,84 +837,84 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", @@ -846,224 +929,230 @@ var swaggerSpec = []string{ "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", - "ldUBAA==", + "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", + "9Gj6YPT+/wUAAP//psDgLSLlAQA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 7cdb8b77ec..437aa136c2 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -1190,10 +1190,6 @@ func (v2 *Handlers) RawTransactionAsync(ctx echo.Context) error { // AccountAssetsInformation looks up an account's asset holdings. // (GET /v2/accounts/{address}/assets) func (v2 *Handlers) AccountAssetsInformation(ctx echo.Context, address basics.Address, params model.AccountAssetsInformationParams) error { - if !v2.Node.Config().EnableExperimentalAPI { - return ctx.String(http.StatusNotFound, "/v2/accounts/{address}/assets was not enabled in the configuration file by setting the EnableExperimentalAPI to true") - } - var assetGreaterThan uint64 = 0 if params.Next != nil { agt, err0 := strconv.ParseUint(*params.Next, 10, 64) @@ -1274,10 +1270,6 @@ func (v2 *Handlers) AccountAssetsInformation(ctx echo.Context, address basics.Ad // AccountApplicationsInformation returns application resources for a specific address. func (v2 *Handlers) AccountApplicationsInformation(ctx echo.Context, address basics.Address, params model.AccountApplicationsInformationParams) error { - if !v2.Node.Config().EnableExperimentalAPI { - return ctx.String(http.StatusNotFound, "/v2/accounts/{address}/applications was not enabled in the configuration file by setting the EnableExperimentalAPI to true") - } - var appGreaterThan uint64 = 0 if params.Next != nil { agt, err0 := strconv.ParseUint(*params.Next, 10, 64) diff --git a/daemon/algod/api/server/v2/test/handlers_resources_test.go b/daemon/algod/api/server/v2/test/handlers_resources_test.go index 116d1cb9c7..6753001620 100644 --- a/daemon/algod/api/server/v2/test/handlers_resources_test.go +++ b/daemon/algod/api/server/v2/test/handlers_resources_test.go @@ -337,7 +337,6 @@ func setupTestForLargeResources(t *testing.T, acctSize, maxResults int, accountM mockNode := makeMockNode(&ml, t.Name(), nil, cannedStatusReportGolden, false) mockNode.config.MaxAPIResourcesPerAccount = uint64(maxResults) - mockNode.config.EnableExperimentalAPI = true dummyShutdownChan := make(chan struct{}) handlers = v2.Handlers{ Node: mockNode, diff --git a/ledger/acctdeltas_test.go b/ledger/acctdeltas_test.go index 0bf62c2197..6efec70595 100644 --- a/ledger/acctdeltas_test.go +++ b/ledger/acctdeltas_test.go @@ -3675,3 +3675,468 @@ func TestOnlineAccountsSuspended(t *testing.T) { require.NoError(t, err) require.Len(t, updated, 0) } + +// TestLookupAssetResourcesWithDeltas verifies that lookupAssetResources properly merges +// in-memory deltas with database results to return current-round data. +// It commits resources to DB, then adds uncommitted delta modifications across two rounds, +// and checks the merged view covers: new creations, holding deletions, holding modifications, +// params-only modifications, params deletions with holding retained, and multi-round +// backwards walking that picks the most recent delta. +func TestLookupAssetResourcesWithDeltas(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + testProtocolVersion := protocol.ConsensusCurrentVersion + protoParams := config.Consensus[testProtocolVersion] + + accts := setupAccts(5) + + var creatorAddr, optinAddr basics.Address + for addr := range accts[0] { + if addr != testSinkAddr && addr != testPoolAddr { + if creatorAddr.IsZero() { + creatorAddr = addr + } else { + optinAddr = addr + break + } + } + } + + ml := makeMockLedgerForTracker(t, true, 1, testProtocolVersion, accts) + defer ml.Close() + + conf := config.GetDefaultLocal() + // Zero lookback so all committed rounds flush immediately, leaving deltas[0] + // as the first uncommitted round. This is critical for testing that the delta + // loop visits index 0. + conf.MaxAcctLookback = 0 + au, _ := newAcctUpdates(t, ml, conf) + + knownCreatables := make(map[basics.CreatableIndex]bool) + + // Round 1: create assets 1000-1005 with params and holdings + // 1000: will have holding modified, then overridden in a second delta round + // 1001: will have holding deleted in delta (params remain since account is creator) + // 1002: will remain unchanged + // 1003: will have params-only modification in delta + // 1004: will have params deleted in delta (holding remains) + // 1005: will have both holding and params deleted in delta + // 1007: will be fully deleted in a committed round; optinAddr opts in with zero balance + { + var updates ledgercore.AccountDeltas + updates.Upsert(creatorAddr, ledgercore.AccountData{ + AccountBaseData: ledgercore.AccountBaseData{ + MicroAlgos: basics.MicroAlgos{Raw: 1_000_000}, + TotalAssetParams: 7, + TotalAssets: 7, + }, + }) + for assetIdx := uint64(1000); assetIdx <= 1005; assetIdx++ { + updates.UpsertAssetResource(creatorAddr, basics.AssetIndex(assetIdx), + ledgercore.AssetParamsDelta{ + Params: &basics.AssetParams{ + Total: assetIdx * 1000, + UnitName: fmt.Sprintf("A%d", assetIdx), + AssetName: fmt.Sprintf("Asset%d", assetIdx), + }, + }, + ledgercore.AssetHoldingDelta{ + Holding: &basics.AssetHolding{Amount: assetIdx * 100}, + }) + } + + // 1007: creatorAddr is creator; optinAddr opts in with zero balance and asset will be deleted + // in a committed round to test that surviving holdings of deleted assets are returned. + updates.UpsertAssetResource(creatorAddr, basics.AssetIndex(1007), + ledgercore.AssetParamsDelta{ + Params: &basics.AssetParams{ + Total: 7000, + UnitName: "A1007", + AssetName: "Asset1007", + }, + }, + ledgercore.AssetHoldingDelta{ + Holding: &basics.AssetHolding{Amount: 7000}, + }) + updates.Upsert(optinAddr, ledgercore.AccountData{ + AccountBaseData: ledgercore.AccountBaseData{ + MicroAlgos: basics.MicroAlgos{Raw: 1_000_000}, + TotalAssets: 1, + }, + }) + updates.UpsertAssetResource(optinAddr, basics.AssetIndex(1007), + ledgercore.AssetParamsDelta{}, + ledgercore.AssetHoldingDelta{Holding: &basics.AssetHolding{Amount: 0}}) + + base := accts[0] + newAccts := applyPartialDeltas(base, updates) + accts = append(accts, newAccts) + + opts := auNewBlockOpts{updates, testProtocolVersion, protoParams, knownCreatables} + auNewBlock(t, 1, au, base, opts, nil) + auCommitSync(t, 1, au, ml) + + for assetIdx := uint64(1000); assetIdx <= 1005; assetIdx++ { + knownCreatables[basics.CreatableIndex(assetIdx)] = true + } + knownCreatables[basics.CreatableIndex(1007)] = true + } + + // Round 2 destroys asset 1007; optinAddr's zero holding survives in DB. + // Additional empty rounds (if any) ensure earlier data flushes past MaxAcctLookback. + for i := basics.Round(2); i <= basics.Round(conf.MaxAcctLookback+2); i++ { + var updates ledgercore.AccountDeltas + if i == 2 { + updates.Upsert(creatorAddr, ledgercore.AccountData{ + AccountBaseData: ledgercore.AccountBaseData{ + MicroAlgos: basics.MicroAlgos{Raw: 1_000_000}, + TotalAssetParams: 6, + TotalAssets: 6, + }, + }) + updates.UpsertAssetResource(creatorAddr, basics.AssetIndex(1007), + ledgercore.AssetParamsDelta{Deleted: true}, + ledgercore.AssetHoldingDelta{Deleted: true}) + } + base := accts[i-1] + newAccts := applyPartialDeltas(base, updates) + accts = append(accts, newAccts) + + opts := auNewBlockOpts{updates, testProtocolVersion, protoParams, knownCreatables} + auNewBlock(t, i, au, base, opts, nil) + auCommitSync(t, i, au, ml) + } + + // Delta round 1 (uncommitted) + deltaRound1 := basics.Round(conf.MaxAcctLookback + 3) + { + var updates ledgercore.AccountDeltas + // 1005: delete both holding and params + updates.UpsertAssetResource(creatorAddr, basics.AssetIndex(1005), + ledgercore.AssetParamsDelta{Deleted: true}, + ledgercore.AssetHoldingDelta{Deleted: true}) + // 1006: new creation (not in DB) + updates.UpsertAssetResource(creatorAddr, basics.AssetIndex(1006), + ledgercore.AssetParamsDelta{ + Params: &basics.AssetParams{Total: 6000, UnitName: "A1006"}, + }, + ledgercore.AssetHoldingDelta{ + Holding: &basics.AssetHolding{Amount: 6000}, + }) + // 1001: delete holding + updates.UpsertAssetResource(creatorAddr, basics.AssetIndex(1001), + ledgercore.AssetParamsDelta{}, + ledgercore.AssetHoldingDelta{Deleted: true}) + // 1000: modify holding (will be overridden by delta round 2) + updates.UpsertAssetResource(creatorAddr, basics.AssetIndex(1000), + ledgercore.AssetParamsDelta{}, + ledgercore.AssetHoldingDelta{ + Holding: &basics.AssetHolding{Amount: 9999}, + }) + // 1003: modify params only (holding unchanged) + updates.UpsertAssetResource(creatorAddr, basics.AssetIndex(1003), + ledgercore.AssetParamsDelta{ + Params: &basics.AssetParams{Total: 7777, UnitName: "A1003new"}, + }, + ledgercore.AssetHoldingDelta{}) + // 1004: delete params (holding remains) + updates.UpsertAssetResource(creatorAddr, basics.AssetIndex(1004), + ledgercore.AssetParamsDelta{Deleted: true}, + ledgercore.AssetHoldingDelta{}) + + base := accts[deltaRound1-1] + opts := auNewBlockOpts{updates, testProtocolVersion, protoParams, knownCreatables} + auNewBlock(t, deltaRound1, au, base, opts, nil) + } + + // Delta round 2 (uncommitted): override 1000's holding from round 1 + deltaRound2 := deltaRound1 + 1 + { + var updates ledgercore.AccountDeltas + updates.UpsertAssetResource(creatorAddr, basics.AssetIndex(1000), + ledgercore.AssetParamsDelta{}, + ledgercore.AssetHoldingDelta{ + Holding: &basics.AssetHolding{Amount: 5555}, + }) + + base := accts[deltaRound1-1] + opts := auNewBlockOpts{updates, testProtocolVersion, protoParams, knownCreatables} + auNewBlock(t, deltaRound2, au, base, opts, nil) + } + + resources, rnd, err := au.LookupAssetResources(creatorAddr, 0, 100) + require.NoError(t, err) + require.Equal(t, deltaRound2, rnd) + + // Expected: 1000, 1001, 1002, 1003, 1004, 1006. + // 1005 fully deleted (both holding and params) — should not appear. + require.Len(t, resources, 6) + + assetMap := make(map[basics.AssetIndex]ledgercore.AssetResourceWithIDs) + for _, res := range resources { + assetMap[res.AssetID] = res + } + + // 1000: holding from delta round 2 (most recent), params preserved from DB + require.Equal(t, uint64(5555), assetMap[basics.AssetIndex(1000)].AssetHolding.Amount) + require.NotNil(t, assetMap[basics.AssetIndex(1000)].AssetParams) + require.Equal(t, uint64(1_000_000), assetMap[basics.AssetIndex(1000)].AssetParams.Total) + + // 1001: holding deleted but params remain (account is still creator) + require.Contains(t, assetMap, basics.AssetIndex(1001)) + require.Nil(t, assetMap[basics.AssetIndex(1001)].AssetHolding) + require.NotNil(t, assetMap[basics.AssetIndex(1001)].AssetParams) + require.Equal(t, uint64(1_001_000), assetMap[basics.AssetIndex(1001)].AssetParams.Total) + + // 1002: unchanged from DB + require.Equal(t, uint64(1002*100), assetMap[basics.AssetIndex(1002)].AssetHolding.Amount) + require.NotNil(t, assetMap[basics.AssetIndex(1002)].AssetParams) + require.Equal(t, uint64(1_002_000), assetMap[basics.AssetIndex(1002)].AssetParams.Total) + + // 1003: params updated in delta, holding preserved from DB + require.Equal(t, uint64(1003*100), assetMap[basics.AssetIndex(1003)].AssetHolding.Amount) + require.NotNil(t, assetMap[basics.AssetIndex(1003)].AssetParams) + require.Equal(t, uint64(7777), assetMap[basics.AssetIndex(1003)].AssetParams.Total) + require.Equal(t, "A1003new", assetMap[basics.AssetIndex(1003)].AssetParams.UnitName) + + // 1004: params deleted in delta, holding preserved from DB, no creator + require.Equal(t, uint64(1004*100), assetMap[basics.AssetIndex(1004)].AssetHolding.Amount) + require.Nil(t, assetMap[basics.AssetIndex(1004)].AssetParams) + require.True(t, assetMap[basics.AssetIndex(1004)].Creator.IsZero()) + + // 1005: both holding and params deleted — should not appear + require.NotContains(t, assetMap, basics.AssetIndex(1005)) + + // 1006: new creation from delta + require.Equal(t, uint64(6000), assetMap[basics.AssetIndex(1006)].AssetHolding.Amount) + require.NotNil(t, assetMap[basics.AssetIndex(1006)].AssetParams) + require.Equal(t, uint64(6000), assetMap[basics.AssetIndex(1006)].AssetParams.Total) + + // optinAddr opted in to asset 1007 with a zero balance before it was destroyed. The protocol + // does not track all opt-outs when an asset is deleted (only the creator's holding is + // enforced), so optinAddr's holding persists in the DB even after the asset params are gone. + // The lookup must return a non-nil holding with zero amount and no params or creator. + optinAddrResources, optinAddrRnd, err := au.LookupAssetResources(optinAddr, 0, 100) + require.NoError(t, err) + require.Equal(t, deltaRound2, optinAddrRnd) + require.Len(t, optinAddrResources, 1) + require.Equal(t, basics.AssetIndex(1007), optinAddrResources[0].AssetID) + require.NotNil(t, optinAddrResources[0].AssetHolding) + require.Equal(t, basics.AssetHolding{}, *optinAddrResources[0].AssetHolding) + require.Nil(t, optinAddrResources[0].AssetParams) + require.True(t, optinAddrResources[0].Creator.IsZero()) +} + +// TestLookupApplicationResourcesWithDeltas verifies that lookupApplicationResources properly +// merges in-memory deltas with database results to return current-round data. +// It covers: new creation, local state deletion, local state modification, params-only +// modification, params deletion with local state retained, multi-round backwards walking, +// and the includeParams flag. +func TestLookupApplicationResourcesWithDeltas(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + testProtocolVersion := protocol.ConsensusCurrentVersion + protoParams := config.Consensus[testProtocolVersion] + + accts := setupAccts(5) + + var testAddr basics.Address + for addr := range accts[0] { + if addr != testSinkAddr && addr != testPoolAddr { + testAddr = addr + break + } + } + + ml := makeMockLedgerForTracker(t, true, 1, testProtocolVersion, accts) + defer ml.Close() + + conf := config.GetDefaultLocal() + // Zero lookback so all committed rounds flush immediately, leaving deltas[0] + // as the first uncommitted round. This is critical for testing that the delta + // loop visits index 0. + conf.MaxAcctLookback = 0 + au, _ := newAcctUpdates(t, ml, conf) + + knownCreatables := make(map[basics.CreatableIndex]bool) + + // Round 1: create apps 2000-2005 with params and local state + // 2000: will have local state modified, then overridden in a second delta round + // 2001: will have local state deleted in delta (params remain since account is creator) + // 2002: will remain unchanged + // 2003: will have params-only modification in delta + // 2004: will have params deleted in delta (local state remains) + // 2005: will have both local state and params deleted in delta + { + var updates ledgercore.AccountDeltas + updates.Upsert(testAddr, ledgercore.AccountData{ + AccountBaseData: ledgercore.AccountBaseData{ + MicroAlgos: basics.MicroAlgos{Raw: 1_000_000}, + TotalAppParams: 6, + TotalAppLocalStates: 6, + }, + }) + for appIdx := uint64(2000); appIdx <= 2005; appIdx++ { + updates.UpsertAppResource(testAddr, basics.AppIndex(appIdx), + ledgercore.AppParamsDelta{ + Params: &basics.AppParams{ + ApprovalProgram: []byte{0x06, 0x81, 0x01}, + GlobalState: basics.TealKeyValue{}, + }, + }, + ledgercore.AppLocalStateDelta{ + LocalState: &basics.AppLocalState{ + Schema: basics.StateSchema{NumUint: appIdx - 2000}, + }, + }) + } + + base := accts[0] + newAccts := applyPartialDeltas(base, updates) + accts = append(accts, newAccts) + + opts := auNewBlockOpts{updates, testProtocolVersion, protoParams, knownCreatables} + auNewBlock(t, 1, au, base, opts, nil) + auCommitSync(t, 1, au, ml) + + for appIdx := uint64(2000); appIdx <= 2005; appIdx++ { + knownCreatables[basics.CreatableIndex(appIdx)] = true + } + } + + // Additional empty rounds (if any) ensure earlier data flushes past MaxAcctLookback into DB. + for i := basics.Round(2); i <= basics.Round(conf.MaxAcctLookback+2); i++ { + var updates ledgercore.AccountDeltas + base := accts[i-1] + newAccts := applyPartialDeltas(base, updates) + accts = append(accts, newAccts) + + opts := auNewBlockOpts{updates, testProtocolVersion, protoParams, knownCreatables} + auNewBlock(t, i, au, base, opts, nil) + auCommitSync(t, i, au, ml) + } + + // Delta round 1 (uncommitted) + deltaRound1 := basics.Round(conf.MaxAcctLookback + 3) + { + var updates ledgercore.AccountDeltas + // 2005: delete both local state and params + updates.UpsertAppResource(testAddr, basics.AppIndex(2005), + ledgercore.AppParamsDelta{Deleted: true}, + ledgercore.AppLocalStateDelta{Deleted: true}) + // 2006: new creation (not in DB) + updates.UpsertAppResource(testAddr, basics.AppIndex(2006), + ledgercore.AppParamsDelta{ + Params: &basics.AppParams{ApprovalProgram: []byte{0x06, 0x81, 0x01}}, + }, + ledgercore.AppLocalStateDelta{ + LocalState: &basics.AppLocalState{ + Schema: basics.StateSchema{NumUint: 60}, + }, + }) + // 2001: delete local state + updates.UpsertAppResource(testAddr, basics.AppIndex(2001), + ledgercore.AppParamsDelta{}, + ledgercore.AppLocalStateDelta{Deleted: true}) + // 2000: modify local state (will be overridden by delta round 2) + updates.UpsertAppResource(testAddr, basics.AppIndex(2000), + ledgercore.AppParamsDelta{}, + ledgercore.AppLocalStateDelta{ + LocalState: &basics.AppLocalState{ + Schema: basics.StateSchema{NumUint: 99}, + }, + }) + // 2003: modify params only (local state unchanged) + updates.UpsertAppResource(testAddr, basics.AppIndex(2003), + ledgercore.AppParamsDelta{ + Params: &basics.AppParams{ + ApprovalProgram: []byte{0x06, 0x81, 0x02}, + ClearStateProgram: []byte{0x06, 0x81, 0x01}, + }, + }, + ledgercore.AppLocalStateDelta{}) + // 2004: delete params (local state remains) + updates.UpsertAppResource(testAddr, basics.AppIndex(2004), + ledgercore.AppParamsDelta{Deleted: true}, + ledgercore.AppLocalStateDelta{}) + + base := accts[deltaRound1-1] + opts := auNewBlockOpts{updates, testProtocolVersion, protoParams, knownCreatables} + auNewBlock(t, deltaRound1, au, base, opts, nil) + } + + // Delta round 2 (uncommitted): override 2000's local state from round 1 + deltaRound2 := deltaRound1 + 1 + { + var updates ledgercore.AccountDeltas + updates.UpsertAppResource(testAddr, basics.AppIndex(2000), + ledgercore.AppParamsDelta{}, + ledgercore.AppLocalStateDelta{ + LocalState: &basics.AppLocalState{ + Schema: basics.StateSchema{NumUint: 42}, + }, + }) + + base := accts[deltaRound1-1] + opts := auNewBlockOpts{updates, testProtocolVersion, protoParams, knownCreatables} + auNewBlock(t, deltaRound2, au, base, opts, nil) + } + + // includeParams=true + resources, rnd, err := au.LookupApplicationResources(testAddr, 0, 100, true) + require.NoError(t, err) + require.Equal(t, deltaRound2, rnd) + + // Expected: 2000, 2001, 2002, 2003, 2004, 2006. + // 2005 fully deleted (both local state and params) — should not appear. + require.Len(t, resources, 6) + + appMap := make(map[basics.AppIndex]ledgercore.AppResourceWithIDs) + for _, res := range resources { + appMap[res.AppID] = res + } + + // 2000: local state from delta round 2 (most recent), params preserved from DB + require.Equal(t, uint64(42), appMap[basics.AppIndex(2000)].AppLocalState.Schema.NumUint) + require.NotNil(t, appMap[basics.AppIndex(2000)].AppParams) + require.Equal(t, []byte{0x06, 0x81, 0x01}, appMap[basics.AppIndex(2000)].AppParams.ApprovalProgram) + + // 2001: local state deleted but params remain (account is still creator) + require.Contains(t, appMap, basics.AppIndex(2001)) + require.Nil(t, appMap[basics.AppIndex(2001)].AppLocalState) + require.NotNil(t, appMap[basics.AppIndex(2001)].AppParams) + require.Equal(t, []byte{0x06, 0x81, 0x01}, appMap[basics.AppIndex(2001)].AppParams.ApprovalProgram) + + // 2002: unchanged from DB + require.Equal(t, uint64(2), appMap[basics.AppIndex(2002)].AppLocalState.Schema.NumUint) + require.NotNil(t, appMap[basics.AppIndex(2002)].AppParams) + + // 2003: params updated in delta, local state preserved from DB + require.Equal(t, uint64(3), appMap[basics.AppIndex(2003)].AppLocalState.Schema.NumUint) + require.NotNil(t, appMap[basics.AppIndex(2003)].AppParams) + require.Equal(t, []byte{0x06, 0x81, 0x02}, appMap[basics.AppIndex(2003)].AppParams.ApprovalProgram) + require.Equal(t, []byte{0x06, 0x81, 0x01}, appMap[basics.AppIndex(2003)].AppParams.ClearStateProgram) + + // 2004: params deleted in delta, local state preserved from DB, no creator + require.Equal(t, uint64(4), appMap[basics.AppIndex(2004)].AppLocalState.Schema.NumUint) + require.Nil(t, appMap[basics.AppIndex(2004)].AppParams) + require.True(t, appMap[basics.AppIndex(2004)].Creator.IsZero()) + + // 2005: both local state and params deleted — should not appear + require.NotContains(t, appMap, basics.AppIndex(2005)) + + // 2006: new creation from delta + require.Equal(t, uint64(60), appMap[basics.AppIndex(2006)].AppLocalState.Schema.NumUint) + require.NotNil(t, appMap[basics.AppIndex(2006)].AppParams) + + // includeParams=false should omit AppParams from all results + resourcesNoParams, _, err := au.LookupApplicationResources(testAddr, 0, 100, false) + require.NoError(t, err) + require.Len(t, resourcesNoParams, 5) + + for _, res := range resourcesNoParams { + require.Nil(t, res.AppParams, "AppParams should be nil when includeParams=false") + } +} diff --git a/ledger/acctupdates.go b/ledger/acctupdates.go index 77889289e2..aedf786e15 100644 --- a/ledger/acctupdates.go +++ b/ledger/acctupdates.go @@ -17,10 +17,12 @@ package ledger import ( + "cmp" "context" "errors" "fmt" "io" + "slices" "sort" "strings" "sync" @@ -1216,98 +1218,301 @@ func (au *accountUpdates) lookupResource(rnd basics.Round, addr basics.Address, } } -// lookupAssetResources returns all the resources for a given address, solely based on what is persisted to disk. It does not -// take into account any in-memory deltas; the round number returned is the latest round number that is known to the database. -func (au *accountUpdates) lookupAssetResources(addr basics.Address, assetIDGT basics.AssetIndex, limit uint64) (data []ledgercore.AssetResourceWithIDs, validThrough basics.Round, err error) { - // Look for resources on disk - persistedResources, resourceDbRound, err0 := au.accountsq.LookupLimitedResources(addr, basics.CreatableIndex(assetIDGT), limit, basics.AssetCreatable) - if err0 != nil { - return nil, basics.Round(0), err0 +// lookupAssetResources returns all the asset resources for a given address. +// It merges in-memory deltas with persisted data to provide current-round information. +func (au *accountUpdates) lookupAssetResources(addr basics.Address, assetIDGT basics.AssetIndex, limit uint64) ([]ledgercore.AssetResourceWithIDs, basics.Round, error) { + needUnlock := true + au.accountsMu.RLock() + defer func() { + if needUnlock { + au.accountsMu.RUnlock() + } + }() + + if limit == 0 { + return nil, au.cachedDBRound + basics.Round(len(au.deltas)), nil } - data = make([]ledgercore.AssetResourceWithIDs, 0, len(persistedResources)) - for _, pd := range persistedResources { - ah := pd.Data.GetAssetHolding() + for { + currentDBRound := au.cachedDBRound + currentDeltaLen := len(au.deltas) - var arwi ledgercore.AssetResourceWithIDs - if !pd.Creator.IsZero() { - ap := pd.Data.GetAssetParams() + // Walk deltas backwards; the first entry found for a given asset is the most recent. + deltaResults := make(map[basics.AssetIndex]ledgercore.AssetResourceRecord) + numDeltaDeleted := 0 - arwi = ledgercore.AssetResourceWithIDs{ - AssetID: basics.AssetIndex(pd.Aidx), - Creator: pd.Creator, + for i := currentDeltaLen; i > 0; { + i-- + for _, rec := range au.deltas[i].Accts.AssetResources { + if rec.Addr != addr || rec.Aidx <= assetIDGT { + continue + } + if _, ok := deltaResults[rec.Aidx]; ok { + continue + } + deltaResults[rec.Aidx] = rec + if rec.Holding.Deleted { + numDeltaDeleted++ + } + } + } + + retRound := currentDBRound + basics.Round(currentDeltaLen) + + au.accountsMu.RUnlock() + needUnlock = false - AssetResource: ledgercore.AssetResource{ - AssetHolding: &ah, - AssetParams: &ap, - }, + // Over-request from DB to compensate for delta deletions that remove DB rows + // from the result set. Deletions are the only delta entries that shrink the + // page — modifications and new creations cannot reduce the DB contribution. + dbLimit := limit + uint64(numDeltaDeleted) + + persistedResources, resourceDbRound, err := au.accountsq.LookupLimitedResources(addr, basics.CreatableIndex(assetIDGT), dbLimit, basics.AssetCreatable) + if err != nil { + return nil, 0, err + } + + if resourceDbRound == currentDBRound { + seenInDB := make(map[basics.AssetIndex]bool, len(persistedResources)) + result := make([]ledgercore.AssetResourceWithIDs, 0, limit) + + // Determine the upper bound of the DB page so we only add delta entries + // within range and don't accidentally set a next-token that skips items. + var dbHasMore bool + var dbMaxID basics.AssetIndex + if len(persistedResources) > 0 { + dbMaxID = basics.AssetIndex(persistedResources[len(persistedResources)-1].Aidx) + dbHasMore = uint64(len(persistedResources)) == dbLimit } - } else { - arwi = ledgercore.AssetResourceWithIDs{ - AssetID: basics.AssetIndex(pd.Aidx), - AssetResource: ledgercore.AssetResource{ - AssetHolding: &ah, - }, + for _, pd := range persistedResources { + assetID := basics.AssetIndex(pd.Aidx) + seenInDB[assetID] = true + + d, inDelta := deltaResults[assetID] + + arwi := ledgercore.AssetResourceWithIDs{AssetID: assetID} + + if inDelta && d.Holding.Deleted { + // Holding removed by delta — leave AssetHolding nil. + } else if inDelta && d.Holding.Holding != nil { + arwi.AssetHolding = d.Holding.Holding + } else { + ah := pd.Data.GetAssetHolding() + arwi.AssetHolding = &ah + } + + if inDelta && d.Params.Deleted { + // Delta deleted params — omit creator and params. + } else if inDelta && d.Params.Params != nil { + arwi.Creator = pd.Creator + arwi.AssetParams = d.Params.Params + } else if !pd.Creator.IsZero() { + arwi.Creator = pd.Creator + ap := pd.Data.GetAssetParams() + arwi.AssetParams = &ap + } + + if arwi.AssetHolding != nil || arwi.AssetParams != nil { + result = append(result, arwi) + } } + + // Add assets that exist only in deltas (new creations not yet in DB). + // Only include delta entries within the DB page range to avoid setting + // a next-token that would skip items still in the database. + for assetID, d := range deltaResults { + if seenInDB[assetID] { + continue + } + if dbHasMore && assetID > dbMaxID { + continue + } + arwi := ledgercore.AssetResourceWithIDs{AssetID: assetID} + if !d.Holding.Deleted && d.Holding.Holding != nil { + arwi.AssetHolding = d.Holding.Holding + } + if !d.Params.Deleted && d.Params.Params != nil { + arwi.Creator = addr + arwi.AssetParams = d.Params.Params + } + if arwi.AssetHolding != nil || arwi.AssetParams != nil { + result = append(result, arwi) + } + } + + slices.SortFunc(result, func(a, b ledgercore.AssetResourceWithIDs) int { + return cmp.Compare(a.AssetID, b.AssetID) + }) + if uint64(len(result)) > limit { + result = result[:limit] + } + + return result, retRound, nil } - data = append(data, arwi) - } - // We've found all the resources we could find for this address. - currentDbRound := resourceDbRound - // The resourceDbRound will not be set if there are no persisted resources - if len(data) == 0 { + if resourceDbRound < currentDBRound { + au.log.Errorf("accountUpdates.lookupAssetResources: database round %d is behind in-memory round %d", resourceDbRound, currentDBRound) + return nil, 0, &StaleDatabaseRoundError{databaseRound: resourceDbRound, memoryRound: currentDBRound} + } au.accountsMu.RLock() - currentDbRound = au.cachedDBRound - au.accountsMu.RUnlock() + needUnlock = true + for currentDBRound >= au.cachedDBRound && currentDeltaLen == len(au.deltas) { + au.accountsReadCond.Wait() + } } - return data, currentDbRound, nil } -// lookupApplicationResources returns all the application resources for a given address, solely based on what is persisted to disk. -// It does not take into account any in-memory deltas; the round number returned is the latest round number that is known to the database. +// lookupApplicationResources returns all the application resources for a given address. +// It merges in-memory deltas with persisted data to provide current-round information. // If includeParams is false, AppParams will not be populated to save memory allocations (app params can be ~50KB each). -func (au *accountUpdates) lookupApplicationResources(addr basics.Address, appIDGT basics.AppIndex, limit uint64, includeParams bool) (data []ledgercore.AppResourceWithIDs, validThrough basics.Round, err error) { - // Look for resources on disk - persistedResources, resourceDbRound, err0 := au.accountsq.LookupLimitedResources(addr, basics.CreatableIndex(appIDGT), limit, basics.AppCreatable) - if err0 != nil { - return nil, basics.Round(0), err0 +func (au *accountUpdates) lookupApplicationResources(addr basics.Address, appIDGT basics.AppIndex, limit uint64, includeParams bool) ([]ledgercore.AppResourceWithIDs, basics.Round, error) { + needUnlock := true + au.accountsMu.RLock() + defer func() { + if needUnlock { + au.accountsMu.RUnlock() + } + }() + + if limit == 0 { + return nil, au.cachedDBRound + basics.Round(len(au.deltas)), nil } - data = make([]ledgercore.AppResourceWithIDs, 0, len(persistedResources)) - for _, pd := range persistedResources { - als := pd.Data.GetAppLocalState() + for { + currentDBRound := au.cachedDBRound + currentDeltaLen := len(au.deltas) + + // Walk deltas backwards; the first entry found for a given app is the most recent. + deltaResults := make(map[basics.AppIndex]ledgercore.AppResourceRecord) + numDeltaDeleted := 0 - arwi := ledgercore.AppResourceWithIDs{ - AppID: basics.AppIndex(pd.Aidx), - AppResource: ledgercore.AppResource{ - AppLocalState: &als, - }, + for i := currentDeltaLen; i > 0; { + i-- + for _, rec := range au.deltas[i].Accts.AppResources { + if rec.Addr != addr || rec.Aidx <= appIDGT { + continue + } + if _, ok := deltaResults[rec.Aidx]; ok { + continue + } + deltaResults[rec.Aidx] = rec + if rec.State.Deleted { + numDeltaDeleted++ + } + } + } + + retRound := currentDBRound + basics.Round(currentDeltaLen) + + au.accountsMu.RUnlock() + needUnlock = false + + // Over-request from DB to compensate for delta deletions that remove DB rows + // from the result set. Deletions are the only delta entries that shrink the + // page — modifications and new creations cannot reduce the DB contribution. + dbLimit := limit + uint64(numDeltaDeleted) + + persistedResources, resourceDbRound, err := au.accountsq.LookupLimitedResources(addr, basics.CreatableIndex(appIDGT), dbLimit, basics.AppCreatable) + if err != nil { + return nil, 0, err } - if !pd.Creator.IsZero() { - arwi.Creator = pd.Creator + if resourceDbRound == currentDBRound { + seenInDB := make(map[basics.AppIndex]bool, len(persistedResources)) + result := make([]ledgercore.AppResourceWithIDs, 0, limit) - // Only populate AppParams if requested to avoid unnecessary memory allocations - // (app params can be ~50KB each, vs ~500 bytes for asset params) - if includeParams { - ap := pd.Data.GetAppParams() - arwi.AppResource.AppParams = &ap + // Determine the upper bound of the DB page so we only add delta entries + // within range and don't accidentally set a next-token that skips items. + var dbHasMore bool + var dbMaxID basics.AppIndex + if len(persistedResources) > 0 { + dbMaxID = basics.AppIndex(persistedResources[len(persistedResources)-1].Aidx) + dbHasMore = uint64(len(persistedResources)) == dbLimit } + + for _, pd := range persistedResources { + appID := basics.AppIndex(pd.Aidx) + seenInDB[appID] = true + + d, inDelta := deltaResults[appID] + + arwi := ledgercore.AppResourceWithIDs{AppID: appID} + + if inDelta && d.State.Deleted { + // Local state removed by delta — leave AppLocalState nil. + } else if inDelta && d.State.LocalState != nil { + arwi.AppLocalState = d.State.LocalState + } else { + als := pd.Data.GetAppLocalState() + arwi.AppLocalState = &als + } + + if inDelta && d.Params.Deleted { + // Delta deleted params — omit creator and params. + } else if inDelta && d.Params.Params != nil { + arwi.Creator = pd.Creator + if includeParams { + arwi.AppResource.AppParams = d.Params.Params + } + } else if !pd.Creator.IsZero() { + arwi.Creator = pd.Creator + if includeParams { + ap := pd.Data.GetAppParams() + arwi.AppResource.AppParams = &ap + } + } + + if arwi.AppLocalState != nil || arwi.AppParams != nil { + result = append(result, arwi) + } + } + + // Add apps that exist only in deltas (new opt-ins not yet in DB). + // Only include delta entries within the DB page range to avoid setting + // a next-token that would skip items still in the database. + for appID, d := range deltaResults { + if seenInDB[appID] { + continue + } + if dbHasMore && appID > dbMaxID { + continue + } + arwi := ledgercore.AppResourceWithIDs{AppID: appID} + if !d.State.Deleted && d.State.LocalState != nil { + arwi.AppLocalState = d.State.LocalState + } + if !d.Params.Deleted && d.Params.Params != nil { + arwi.Creator = addr + if includeParams { + arwi.AppResource.AppParams = d.Params.Params + } + } + if arwi.AppLocalState != nil || arwi.AppParams != nil { + result = append(result, arwi) + } + } + + slices.SortFunc(result, func(a, b ledgercore.AppResourceWithIDs) int { + return cmp.Compare(a.AppID, b.AppID) + }) + if uint64(len(result)) > limit { + result = result[:limit] + } + + return result, retRound, nil } - data = append(data, arwi) - } - // We've found all the resources we could find for this address. - currentDbRound := resourceDbRound - // The resourceDbRound will not be set if there are no persisted resources - if len(data) == 0 { + if resourceDbRound < currentDBRound { + au.log.Errorf("accountUpdates.lookupApplicationResources: database round %d is behind in-memory round %d", resourceDbRound, currentDBRound) + return nil, 0, &StaleDatabaseRoundError{databaseRound: resourceDbRound, memoryRound: currentDBRound} + } au.accountsMu.RLock() - currentDbRound = au.cachedDBRound - au.accountsMu.RUnlock() + needUnlock = true + for currentDBRound >= au.cachedDBRound && currentDeltaLen == len(au.deltas) { + au.accountsReadCond.Wait() + } } - return data, currentDbRound, nil } func (au *accountUpdates) lookupStateDelta(rnd basics.Round) (ledgercore.StateDelta, error) { diff --git a/ledger/ledger.go b/ledger/ledger.go index a0291f31cd..9cf818c7e6 100644 --- a/ledger/ledger.go +++ b/ledger/ledger.go @@ -597,6 +597,9 @@ func (l *Ledger) LookupAsset(rnd basics.Round, addr basics.Address, aidx basics. // LookupAssets loads asset resources that match the request parameters from the ledger. func (l *Ledger) LookupAssets(addr basics.Address, assetIDGT basics.AssetIndex, limit uint64) ([]ledgercore.AssetResourceWithIDs, basics.Round, error) { + l.trackerMu.RLock() + defer l.trackerMu.RUnlock() + resources, lookupRound, err := l.accts.LookupAssetResources(addr, assetIDGT, limit) return resources, lookupRound, err } @@ -604,8 +607,10 @@ func (l *Ledger) LookupAssets(addr basics.Address, assetIDGT basics.AssetIndex, // LookupApplications returns the application resources (local state and params) for a given address, with pagination support. // If includeParams is false, AppParams will not be populated to save memory allocations. func (l *Ledger) LookupApplications(addr basics.Address, appIDGT basics.AppIndex, limit uint64, includeParams bool) ([]ledgercore.AppResourceWithIDs, basics.Round, error) { - resources, lookupRound, err := l.accts.LookupApplicationResources(addr, appIDGT, limit, includeParams) - return resources, lookupRound, err + l.trackerMu.RLock() + defer l.trackerMu.RUnlock() + + return l.accts.LookupApplicationResources(addr, appIDGT, limit, includeParams) } // lookupResource loads a resource that matches the request parameters from the accounts update