Skip to content

Commit 5d1d36e

Browse files
authored
ledger: fix duplicate empty rows for suspended accounts (#6314)
1 parent e4c416a commit 5d1d36e

File tree

2 files changed

+92
-1
lines changed

2 files changed

+92
-1
lines changed

ledger/acctdeltas.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1032,7 +1032,12 @@ func onlineAccountsNewRoundImpl(
10321032
prevAcct = updated
10331033
}
10341034
} else {
1035-
if prevAcct.AccountData.IsVotingEmpty() && newAcct.IsVotingEmpty() {
1035+
if prevAcct.AccountData.IsVotingEmpty() && newStatus != basics.Online {
1036+
// we are not using newAcct.IsVotingEmpty because new account comes from deltas,
1037+
// and deltas are base (full) accounts, so that it can have status=offline and non-empty voting data
1038+
// for suspended accounts.
1039+
// it is not the same for online accounts where empty all offline accounts are stored with empty voting data.
1040+
10361041
// if both old and new are offline, ignore
10371042
// otherwise the following could happen:
10381043
// 1. there are multiple offline account deltas so all of them could be inserted

ledger/acctdeltas_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3598,3 +3598,89 @@ func TestOnlineAccountsExceedOfflineRows(t *testing.T) {
35983598
require.True(t, history[1].AccountData.IsVotingEmpty())
35993599
require.Equal(t, basics.Round(5), history[1].UpdRound)
36003600
}
3601+
3602+
// TestOnlineAccountsSuspended checks that transfer to suspended account does not produce extra rows
3603+
// in online accounts table. The test is similar to TestOnlineAccountsExceedOfflineRows.
3604+
func TestOnlineAccountsSuspended(t *testing.T) {
3605+
partitiontest.PartitionTest(t)
3606+
t.Parallel()
3607+
3608+
dbs, _ := storetesting.DbOpenTest(t, true)
3609+
storetesting.SetDbLogging(t, dbs)
3610+
defer dbs.Close()
3611+
3612+
tx, err := dbs.Wdb.Handle.Begin()
3613+
require.NoError(t, err)
3614+
defer tx.Rollback()
3615+
3616+
proto := config.Consensus[protocol.ConsensusCurrentVersion]
3617+
3618+
var accts map[basics.Address]basics.AccountData
3619+
sqlitedriver.AccountsInitTest(t, tx, accts, protocol.ConsensusCurrentVersion)
3620+
3621+
addrA := ledgertesting.RandomAddress()
3622+
3623+
deltaA := onlineAccountDelta{
3624+
address: addrA,
3625+
newAcct: []trackerdb.BaseOnlineAccountData{
3626+
{
3627+
MicroAlgos: basics.MicroAlgos{Raw: 100_000_000},
3628+
IncentiveEligible: true, // does not matter for commit logic but makes the test intent clearer
3629+
BaseVotingData: trackerdb.BaseVotingData{VoteFirstValid: 1, VoteLastValid: 5},
3630+
},
3631+
// suspend, offline but non-empty voting data
3632+
{
3633+
MicroAlgos: basics.MicroAlgos{Raw: 100_000_000},
3634+
IncentiveEligible: false,
3635+
BaseVotingData: trackerdb.BaseVotingData{VoteFirstValid: 1, VoteLastValid: 5},
3636+
},
3637+
},
3638+
updRound: []uint64{1, 2},
3639+
newStatus: []basics.Status{basics.Online, basics.Offline},
3640+
}
3641+
updates := compactOnlineAccountDeltas{}
3642+
updates.deltas = append(updates.deltas, deltaA)
3643+
writer, err := sqlitedriver.MakeOnlineAccountsSQLWriter(tx, updates.len() > 0)
3644+
require.NoError(t, err)
3645+
defer writer.Close()
3646+
3647+
lastUpdateRound := basics.Round(2)
3648+
updated, err := onlineAccountsNewRoundImpl(writer, updates, proto, lastUpdateRound)
3649+
require.NoError(t, err)
3650+
require.Len(t, updated, 2)
3651+
3652+
var baseOnlineAccounts lruOnlineAccounts
3653+
baseOnlineAccounts.init(logging.TestingLog(t), 1000, 800)
3654+
for _, persistedAcct := range updated {
3655+
baseOnlineAccounts.write(persistedAcct)
3656+
}
3657+
3658+
// make sure baseOnlineAccounts has the entry
3659+
entry, has := baseOnlineAccounts.read(addrA)
3660+
require.True(t, has)
3661+
require.True(t, entry.AccountData.IsVotingEmpty())
3662+
require.Equal(t, basics.Round(2), entry.UpdRound)
3663+
3664+
acctDelta := ledgercore.AccountDeltas{}
3665+
3666+
// simulate transfer to suspended account
3667+
ad := ledgercore.AccountData{
3668+
AccountBaseData: ledgercore.AccountBaseData{
3669+
Status: basics.Offline,
3670+
MicroAlgos: basics.MicroAlgos{Raw: 100_000_000 - 1},
3671+
},
3672+
VotingData: basics.VotingData{
3673+
VoteFirstValid: 1,
3674+
VoteLastValid: 5,
3675+
},
3676+
}
3677+
acctDelta.Upsert(addrA, ad)
3678+
deltas := []ledgercore.AccountDeltas{acctDelta}
3679+
updates = makeCompactOnlineAccountDeltas(deltas, 3, baseOnlineAccounts)
3680+
3681+
// insert and make sure no new rows are inserted
3682+
lastUpdateRound = basics.Round(3)
3683+
updated, err = onlineAccountsNewRoundImpl(writer, updates, proto, lastUpdateRound)
3684+
require.NoError(t, err)
3685+
require.Len(t, updated, 0)
3686+
}

0 commit comments

Comments
 (0)