diff --git a/ledger/eval.go b/ledger/eval.go index 20b5a3cd32..533525e8e4 100644 --- a/ledger/eval.go +++ b/ledger/eval.go @@ -1294,6 +1294,21 @@ type loadedTransactionGroup struct { err error } +// Return the maximum number of addresses referenced in any given transaction. +func maxAddressesInTxn(proto *config.ConsensusParams) int { + return 7 + proto.MaxAppTxnAccounts +} + +// Write the list of addresses referenced in `txn` to `out`. Addresses might repeat. +func getTxnAddresses(txn *transactions.Transaction, out *[]basics.Address) { + *out = (*out)[:0] + + *out = append( + *out, txn.Sender, txn.Receiver, txn.CloseRemainderTo, txn.AssetSender, + txn.AssetReceiver, txn.AssetCloseTo, txn.FreezeAccount) + *out = append(*out, txn.ApplicationCallTxnFields.Accounts...) +} + // loadAccounts loads the account data for the provided transaction group list. It also loads the feeSink account and add it to the first returned transaction group. // The order of the transaction groups returned by the channel is identical to the one in the input array. func loadAccounts(ctx context.Context, l ledgerForEvaluator, rnd basics.Round, groups [][]transactions.SignedTxnWithAD, feeSinkAddr basics.Address, consensusParams config.ConsensusParams) chan loadedTransactionGroup { @@ -1320,8 +1335,7 @@ func loadAccounts(ctx context.Context, l ledgerForEvaluator, rnd basics.Round, g defer close(outChan) accountTasks := make(map[basics.Address]*addrTask) - maxAddressesPerTransaction := 7 + consensusParams.MaxAppTxnAccounts - addressesCh := make(chan *addrTask, len(groups)*consensusParams.MaxTxGroupSize*maxAddressesPerTransaction) + addressesCh := make(chan *addrTask, len(groups)*consensusParams.MaxTxGroupSize*maxAddressesInTxn(&consensusParams)) // totalBalances counts the total number of balances over all the transaction groups totalBalances := 0 @@ -1362,6 +1376,7 @@ func loadAccounts(ctx context.Context, l ledgerForEvaluator, rnd basics.Round, g task := &groupTask{} groupsReady[i] = task for _, stxn := range group { + // If you add new addresses here, also add them in getTxnAddresses(). initAccount(stxn.Txn.Sender, task) initAccount(stxn.Txn.Receiver, task) initAccount(stxn.Txn.CloseRemainderTo, task) @@ -1503,6 +1518,24 @@ func (vb ValidatedBlock) WithSeed(s committee.Seed) ValidatedBlock { } } +// GetBlockAddresses returns all addresses referenced in `block`. +func GetBlockAddresses(block *bookkeeping.Block) map[basics.Address]struct{} { + // Reserve a reasonable memory size for the map. + res := make(map[basics.Address]struct{}, len(block.Payset)+2) + res[block.FeeSink] = struct{}{} + res[block.RewardsPool] = struct{}{} + + var refAddresses []basics.Address + for _, stib := range block.Payset { + getTxnAddresses(&stib.Txn, &refAddresses) + for _, address := range refAddresses { + res[address] = struct{}{} + } + } + + return res +} + // Eval evaluates a block without validation using the given `proto`. Return the state // delta and transactions with modified apply data according to `proto`. // This function is used by Indexer which modifies `proto` to retrieve the asset