@@ -2357,6 +2357,74 @@ func TestAcctUpdatesLookupRetry(t *testing.T) {
23572357 })
23582358}
23592359
2360+ // auCommitSync is a helper function calling the committing sequence similarly to what tracker registry does
2361+ func auCommitSync (t * testing.T , rnd basics.Round , au * accountUpdates , ml * mockLedgerForTracker ) {
2362+ _ , maxLookback := au .committedUpTo (rnd )
2363+ dcc := & deferredCommitContext {
2364+ deferredCommitRange : deferredCommitRange {
2365+ lookback : maxLookback ,
2366+ },
2367+ }
2368+ cdr := & dcc .deferredCommitRange
2369+ cdr = au .produceCommittingTask (rnd , ml .trackers .dbRound , cdr )
2370+ if cdr != nil {
2371+ func () {
2372+ dcc .deferredCommitRange = * cdr
2373+ ml .trackers .accountsWriting .Add (1 )
2374+ defer ml .trackers .accountsWriting .Done ()
2375+
2376+ // do not take any locks since all operations are synchronous
2377+ newBase := basics .Round (dcc .offset ) + dcc .oldBase
2378+ dcc .newBase = newBase
2379+
2380+ err := au .prepareCommit (dcc )
2381+ require .NoError (t , err )
2382+ err = ml .trackers .dbs .Wdb .Atomic (func (ctx context.Context , tx * sql.Tx ) (err error ) {
2383+ err = au .commitRound (ctx , tx , dcc )
2384+ if err != nil {
2385+ return err
2386+ }
2387+ err = updateAccountsRound (tx , newBase )
2388+ return err
2389+ })
2390+ require .NoError (t , err )
2391+ ml .trackers .dbRound = newBase
2392+ au .postCommit (ml .trackers .ctx , dcc )
2393+ au .postCommitUnlocked (ml .trackers .ctx , dcc )
2394+ }()
2395+ }
2396+ }
2397+
2398+ type auNewBlockOpts struct {
2399+ updates ledgercore.AccountDeltas
2400+ version protocol.ConsensusVersion
2401+ protoParams config.ConsensusParams
2402+ knownCreatables map [basics.CreatableIndex ]bool
2403+ }
2404+
2405+ func auNewBlock (t * testing.T , rnd basics.Round , au * accountUpdates , base map [basics.Address ]basics.AccountData , data auNewBlockOpts ) {
2406+ rewardLevel := uint64 (0 )
2407+ prevRound , prevTotals , err := au .LatestTotals ()
2408+ require .Equal (t , rnd - 1 , prevRound )
2409+ require .NoError (t , err )
2410+
2411+ newTotals := ledgertesting .CalculateNewRoundAccountTotals (t , data .updates , rewardLevel , data .protoParams , base , prevTotals )
2412+
2413+ blk := bookkeeping.Block {
2414+ BlockHeader : bookkeeping.BlockHeader {
2415+ Round : basics .Round (rnd ),
2416+ },
2417+ }
2418+ blk .RewardsLevel = rewardLevel
2419+ blk .CurrentProtocol = data .version
2420+ delta := ledgercore .MakeStateDelta (& blk .BlockHeader , 0 , data .updates .Len (), 0 )
2421+ delta .Accts .MergeAccounts (data .updates )
2422+ delta .Creatables = creatablesFromUpdates (base , data .updates , data .knownCreatables )
2423+ delta .Totals = newTotals
2424+
2425+ au .newBlock (blk , delta )
2426+ }
2427+
23602428// TestAcctUpdatesLookupLatestCacheRetry simulates a situation when base account and resources are in a cache but
23612429// account updates advances while calling lookupLatest
23622430// The idea of the test:
@@ -2408,67 +2476,6 @@ func TestAcctUpdatesLookupLatestCacheRetry(t *testing.T) {
24082476 aidx2 := basics .AssetIndex (2 )
24092477 knownCreatables := make (map [basics.CreatableIndex ]bool )
24102478
2411- commitSync := func (rnd basics.Round ) {
2412- _ , maxLookback := au .committedUpTo (rnd )
2413- dcc := & deferredCommitContext {
2414- deferredCommitRange : deferredCommitRange {
2415- lookback : maxLookback ,
2416- },
2417- }
2418- cdr := & dcc .deferredCommitRange
2419- cdr = au .produceCommittingTask (rnd , ml .trackers .dbRound , cdr )
2420- if cdr != nil {
2421- func () {
2422- dcc .deferredCommitRange = * cdr
2423- ml .trackers .accountsWriting .Add (1 )
2424- defer ml .trackers .accountsWriting .Done ()
2425-
2426- // do not take any locks since all operations are synchronous
2427- newBase := basics .Round (dcc .offset ) + dcc .oldBase
2428- dcc .newBase = newBase
2429-
2430- err := au .prepareCommit (dcc )
2431- require .NoError (t , err )
2432- err = ml .trackers .dbs .Wdb .Atomic (func (ctx context.Context , tx * sql.Tx ) (err error ) {
2433- err = au .commitRound (ctx , tx , dcc )
2434- if err != nil {
2435- return err
2436- }
2437- err = updateAccountsRound (tx , newBase )
2438- return err
2439- })
2440- require .NoError (t , err )
2441- ml .trackers .dbRound = newBase
2442- au .postCommit (ml .trackers .ctx , dcc )
2443- au .postCommitUnlocked (ml .trackers .ctx , dcc )
2444- }()
2445-
2446- }
2447- }
2448-
2449- newBlock := func (au * accountUpdates , rnd basics.Round , base map [basics.Address ]basics.AccountData , updates ledgercore.AccountDeltas ) {
2450- rewardLevel := uint64 (0 )
2451- prevRound , prevTotals , err := au .LatestTotals ()
2452- require .Equal (t , rnd - 1 , prevRound )
2453- require .NoError (t , err )
2454-
2455- newTotals := ledgertesting .CalculateNewRoundAccountTotals (t , updates , rewardLevel , protoParams , base , prevTotals )
2456-
2457- blk := bookkeeping.Block {
2458- BlockHeader : bookkeeping.BlockHeader {
2459- Round : basics .Round (rnd ),
2460- },
2461- }
2462- blk .RewardsLevel = rewardLevel
2463- blk .CurrentProtocol = testProtocolVersion
2464- delta := ledgercore .MakeStateDelta (& blk .BlockHeader , 0 , updates .Len (), 0 )
2465- delta .Accts .MergeAccounts (updates )
2466- delta .Creatables = creatablesFromUpdates (base , updates , knownCreatables )
2467- delta .Totals = newTotals
2468-
2469- au .newBlock (blk , delta )
2470- }
2471-
24722479 // the test 1 requires 2 blocks with different resource state, au requires MaxBalLookback block to start persisting
24732480 for i := basics .Round (1 ); i <= basics .Round (protoParams .MaxBalLookback + 2 ); i ++ {
24742481 var updates ledgercore.AccountDeltas
@@ -2485,10 +2492,11 @@ func TestAcctUpdatesLookupLatestCacheRetry(t *testing.T) {
24852492 accts = append (accts , newAccts )
24862493
24872494 // prepare block
2488- newBlock (au , i , base , updates )
2495+ opts := auNewBlockOpts {updates , testProtocolVersion , protoParams , knownCreatables }
2496+ auNewBlock (t , i , au , base , opts )
24892497
24902498 // commit changes synchroniously
2491- commitSync ( i )
2499+ auCommitSync ( t , i , au , ml )
24922500 }
24932501
24942502 // ensure rounds
@@ -2548,8 +2556,9 @@ func TestAcctUpdatesLookupLatestCacheRetry(t *testing.T) {
25482556 au .accountsMu .Lock ()
25492557 au .cachedDBRound = oldCachedDBRound
25502558 au .accountsMu .Unlock ()
2551- newBlock (au , rnd + 1 , accts [rnd ], ledgercore.AccountDeltas {})
2552- commitSync (rnd + 1 )
2559+ opts := auNewBlockOpts {ledgercore.AccountDeltas {}, testProtocolVersion , protoParams , knownCreatables }
2560+ auNewBlock (t , rnd + 1 , au , accts [rnd ], opts )
2561+ auCommitSync (t , rnd + 1 , au , ml )
25532562
25542563 wg .Wait ()
25552564
@@ -2559,3 +2568,91 @@ func TestAcctUpdatesLookupLatestCacheRetry(t *testing.T) {
25592568 require .Equal (t , uint64 (100 ), ad .Assets [aidx1 ].Amount )
25602569 require .Equal (t , uint64 (200 ), ad .Assets [aidx2 ].Amount )
25612570}
2571+
2572+ // TestAcctUpdatesLookupResources creates 3 assets, deletes one
2573+ // and checks au.resources with deleted resources are not counted toward totals
2574+ func TestAcctUpdatesLookupResources (t * testing.T ) {
2575+ partitiontest .PartitionTest (t )
2576+
2577+ accts := []map [basics.Address ]basics.AccountData {ledgertesting .RandomAccounts (1 , true )}
2578+ pooldata := basics.AccountData {}
2579+ pooldata .MicroAlgos .Raw = 100 * 1000 * 1000 * 1000 * 1000
2580+ pooldata .Status = basics .NotParticipating
2581+ accts [0 ][testPoolAddr ] = pooldata
2582+
2583+ sinkdata := basics.AccountData {}
2584+ sinkdata .MicroAlgos .Raw = 1000 * 1000 * 1000 * 1000
2585+ sinkdata .Status = basics .NotParticipating
2586+ accts [0 ][testSinkAddr ] = sinkdata
2587+
2588+ testProtocolVersion := protocol .ConsensusVersion ("test-protocol-TestAcctUpdatesLookupResources" )
2589+ protoParams := config .Consensus [protocol .ConsensusCurrentVersion ]
2590+ protoParams .MaxBalLookback = 2
2591+ protoParams .SeedLookback = 1
2592+ protoParams .SeedRefreshInterval = 1
2593+ config .Consensus [testProtocolVersion ] = protoParams
2594+ defer func () {
2595+ delete (config .Consensus , testProtocolVersion )
2596+ }()
2597+
2598+ ml := makeMockLedgerForTracker (t , true , 1 , testProtocolVersion , accts )
2599+ defer ml .Close ()
2600+
2601+ conf := config .GetDefaultLocal ()
2602+ au := newAcctUpdates (t , ml , conf , "." )
2603+ defer au .close ()
2604+
2605+ var addr1 basics.Address
2606+ for addr := range accts [0 ] {
2607+ if addr != testSinkAddr && addr != testPoolAddr {
2608+ addr1 = addr
2609+ break
2610+ }
2611+ }
2612+
2613+ aidx1 := basics .AssetIndex (1 )
2614+ aidx2 := basics .AssetIndex (2 )
2615+ aidx3 := basics .AssetIndex (3 )
2616+ knownCreatables := make (map [basics.CreatableIndex ]bool )
2617+
2618+ // test requires 5 blocks: 1 with aidx1, protoParams.MaxBalLookback empty blocks to commit the first one
2619+ // and 1 block with aidx2 and aidx3, and another one with aidx2 deleted
2620+ for i := basics .Round (1 ); i <= basics .Round (protoParams .MaxBalLookback + 3 ); i ++ {
2621+ var updates ledgercore.AccountDeltas
2622+
2623+ // add data
2624+ if i == 1 {
2625+ updates .Upsert (addr1 , ledgercore.AccountData {AccountBaseData : ledgercore.AccountBaseData {MicroAlgos : basics.MicroAlgos {Raw : 1000000 }, TotalAssets : 1 }})
2626+ updates .UpsertAssetResource (addr1 , aidx1 , ledgercore.AssetParamsDelta {}, ledgercore.AssetHoldingDelta {Holding : & basics.AssetHolding {Amount : 100 }})
2627+ }
2628+ if i == basics .Round (protoParams .MaxBalLookback + 2 ) {
2629+ updates .Upsert (addr1 , ledgercore.AccountData {AccountBaseData : ledgercore.AccountBaseData {MicroAlgos : basics.MicroAlgos {Raw : 1000000 }, TotalAssets : 3 }})
2630+ updates .UpsertAssetResource (addr1 , aidx2 , ledgercore.AssetParamsDelta {}, ledgercore.AssetHoldingDelta {Holding : & basics.AssetHolding {Amount : 200 }})
2631+ updates .UpsertAssetResource (addr1 , aidx3 , ledgercore.AssetParamsDelta {}, ledgercore.AssetHoldingDelta {Holding : & basics.AssetHolding {Amount : 300 }})
2632+ }
2633+ if i == basics .Round (protoParams .MaxBalLookback + 3 ) {
2634+ updates .Upsert (addr1 , ledgercore.AccountData {AccountBaseData : ledgercore.AccountBaseData {MicroAlgos : basics.MicroAlgos {Raw : 1000000 }, TotalAssets : 2 }})
2635+ updates .UpsertAssetResource (addr1 , aidx2 , ledgercore.AssetParamsDelta {}, ledgercore.AssetHoldingDelta {Deleted : true })
2636+ }
2637+
2638+ base := accts [i - 1 ]
2639+ newAccts := applyPartialDeltas (base , updates )
2640+ accts = append (accts , newAccts )
2641+
2642+ // prepare block
2643+ opts := auNewBlockOpts {updates , testProtocolVersion , protoParams , knownCreatables }
2644+ auNewBlock (t , i , au , base , opts )
2645+
2646+ if i <= basics .Round (protoParams .MaxBalLookback + 1 ) {
2647+ auCommitSync (t , i , au , ml )
2648+ }
2649+ // do not commit two last blocks to keep data in memory deltas
2650+ }
2651+ data , rnd , _ , err := au .lookupLatest (addr1 )
2652+ require .NoError (t , err )
2653+ require .Equal (t , basics .Round (protoParams .MaxBalLookback + 3 ), rnd )
2654+ require .Len (t , data .Assets , 2 )
2655+ require .Contains (t , data .Assets , aidx1 )
2656+ require .Contains (t , data .Assets , aidx3 )
2657+ require .NotContains (t , data .Assets , aidx2 )
2658+ }
0 commit comments