Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions ledger/eval_simple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -825,27 +825,49 @@ func TestDoubleLedgerGetKnockoffCandidates(t *testing.T) {
t.Parallel()

const onlineCount = 5
genBalances, _, _ := ledgertesting.NewTestGenesis(func(cfg *ledgertesting.GenesisCfg) {
genBalances, addrs, _ := ledgertesting.NewTestGenesis(func(cfg *ledgertesting.GenesisCfg) {
cfg.OnlineCount = onlineCount
ledgertesting.TurnOffRewards(cfg)
})
payoutsBegin := 40

// txn to send in round 1, to change the balances to be different from genesis
payTxn := &txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: addrs[2], Amount: 1_000_000}

checkAccts := func(l *Ledger, rnd basics.Round, cv protocol.ConsensusVersion) {
accts, err := l.GetKnockOfflineCandidates(rnd, config.Consensus[cv])
require.NoError(t, err)
require.NotEmpty(t, accts)

// get online genesis accounts
onlineCnt := 0
onlineAddrs := make(map[basics.Address]basics.OnlineAccountData)
genesisOnlineAccts := make(map[basics.Address]basics.OnlineAccountData)
afterPayTxnOnlineAccts := make(map[basics.Address]basics.OnlineAccountData)
for addr, ad := range genBalances.Balances {
if ad.Status == basics.Online {
onlineCnt++
onlineAddrs[addr] = ad.OnlineAccountData()
genesisOnlineAccts[addr] = ad.OnlineAccountData()
afterPayTxnOnlineAccts[addr] = ad.OnlineAccountData()
}
}

// calculate expected balances after applying payTxn
payTxnReceiver := afterPayTxnOnlineAccts[payTxn.Receiver]
payTxnReceiver.MicroAlgosWithRewards.Raw += payTxn.Amount
payTxnSender := afterPayTxnOnlineAccts[payTxn.Sender]
payTxnSender.MicroAlgosWithRewards.Raw -= (payTxn.Amount + config.Consensus[cv].MinTxnFee)
afterPayTxnOnlineAccts[payTxn.Receiver] = payTxnReceiver
afterPayTxnOnlineAccts[payTxn.Sender] = payTxnSender

require.Equal(t, onlineCount, onlineCnt)
require.Len(t, accts, onlineCnt)
require.Equal(t, onlineAddrs, accts)
if rnd == 0 {
// balances should be same as genesis
require.Equal(t, genesisOnlineAccts, accts)
} else {
// balances > rnd 1 should reflect payTxn change
require.Equal(t, afterPayTxnOnlineAccts, accts, "rnd %d", rnd)
}

}

Expand All @@ -856,6 +878,10 @@ func TestDoubleLedgerGetKnockoffCandidates(t *testing.T) {
checkAccts(dl.generator, basics.Round(0), cv)
checkAccts(dl.validator, basics.Round(0), cv)

// change two accounts' balances to be different from genesis
payTxn.GenesisHash = crypto.Digest{} // clear if set from previous run
dl.fullBlock(payTxn)

// run up to round 240
proto := config.Consensus[cv]
upToRound := basics.Round(proto.StateProofInterval - proto.StateProofVotersLookback)
Expand Down
24 changes: 14 additions & 10 deletions ledger/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,27 +653,31 @@ func (l *Ledger) GetKnockOfflineCandidates(rnd basics.Round, proto config.Consen
return nil, nil
}

var addrs []basics.Address

// special handling for rounds 0-240: return participating genesis accounts
if rnd < basics.Round(proto.StateProofInterval).SubSaturate(basics.Round(proto.StateProofVotersLookback)) {
ret := make(map[basics.Address]basics.OnlineAccountData)
for addr, data := range l.genesisAccounts {
if data.Status == basics.Online {
ret[addr] = data.OnlineAccountData()
addrs = append(addrs, addr)
}
}
return ret, nil
}

// get latest state proof voters information, up to rnd, without calling cond.Wait()
_, voters := l.acctsOnline.voters.LatestCompletedVotersUpTo(rnd)
if voters == nil { // no cached voters found < rnd
return nil, nil
} else {
// get latest state proof voters information, up to rnd, without calling cond.Wait()
_, voters := l.acctsOnline.voters.LatestCompletedVotersUpTo(rnd)
if voters == nil { // no cached voters found < rnd
return nil, nil
}
addrs = make([]basics.Address, 0, len(voters.AddrToPos))
for addr := range voters.AddrToPos {
addrs = append(addrs, addr)
}
}

// fetch fresh data up to this round from online account cache. These accounts should all
// be in cache, as long as proto.StateProofTopVoters < onlineAccountsCacheMaxSize.
Comment on lines 677 to 678
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment makes me wonder why the genesis accounts are expected to be here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The online account cache is loaded up with the output of AccountsReaderExt.OnlineAccountsAll at DB load / tracker initialization time (in onlineAccounts.initializeFromDisk)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll merge and clean up comment.

Copy link
Copy Markdown
Contributor Author

@cce cce Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you're wondering how genesis accounts get into the tracker DB tables like the accountbase and onlineaccounts tables in the first place — when trackerDBInitialize sets up a fresh ledger with genesis accounts, it uses them to populate the ledger DB.

ret := make(map[basics.Address]basics.OnlineAccountData)
for addr := range voters.AddrToPos {
for _, addr := range addrs {
data, err := l.acctsOnline.lookupOnlineAccountData(rnd, addr)
if err != nil {
continue // skip missing / not online accounts
Expand Down