From eda1a3ac6426d06d57929647f7366efe9b7be83f Mon Sep 17 00:00:00 2001 From: Tolik Zinovyev Date: Wed, 3 Nov 2021 15:14:04 -0400 Subject: [PATCH] Write app call addresses in txn_participation table. --- accounting/accounting.go | 30 ++++++++++ idb/postgres/internal/writer/writer.go | 23 +------- idb/postgres/internal/writer/writer_test.go | 64 +++++++++++++++++++++ idb/postgres/postgres.go | 20 ++----- 4 files changed, 102 insertions(+), 35 deletions(-) create mode 100644 accounting/accounting.go diff --git a/accounting/accounting.go b/accounting/accounting.go new file mode 100644 index 000000000..de38681f9 --- /dev/null +++ b/accounting/accounting.go @@ -0,0 +1,30 @@ +package accounting + +import ( + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/data/transactions" +) + +// GetTransactionParticipants calls function `add` for every address referenced in the +// given transaction, possibly with repetition. +func GetTransactionParticipants(stxnad *transactions.SignedTxnWithAD, includeInner bool, add func(address basics.Address)) { + txn := &stxnad.Txn + + add(txn.Sender) + add(txn.Receiver) + add(txn.CloseRemainderTo) + add(txn.AssetSender) + add(txn.AssetReceiver) + add(txn.AssetCloseTo) + add(txn.FreezeAccount) + + for _, address := range txn.ApplicationCallTxnFields.Accounts { + add(address) + } + + if includeInner { + for _, inner := range stxnad.ApplyData.EvalDelta.InnerTxns { + GetTransactionParticipants(&inner, includeInner, add) + } + } +} diff --git a/idb/postgres/internal/writer/writer.go b/idb/postgres/internal/writer/writer.go index d1e4cfc94..582890ceb 100644 --- a/idb/postgres/internal/writer/writer.go +++ b/idb/postgres/internal/writer/writer.go @@ -13,6 +13,7 @@ import ( "github.com/algorand/go-algorand/protocol" "github.com/jackc/pgx/v4" + "github.com/algorand/indexer/accounting" "github.com/algorand/indexer/idb" "github.com/algorand/indexer/idb/postgres/internal/encoding" "github.com/algorand/indexer/idb/postgres/internal/schema" @@ -265,24 +266,6 @@ func (w *Writer) addTransactions(block *bookkeeping.Block, modifiedTxns []transa return nil } -func getTransactionParticipantsImpl(stxnad *transactions.SignedTxnWithAD, includeInner bool, add func(address basics.Address)) { - txn := stxnad.Txn - - add(txn.Sender) - add(txn.Receiver) - add(txn.CloseRemainderTo) - add(txn.AssetSender) - add(txn.AssetReceiver) - add(txn.AssetCloseTo) - add(txn.FreezeAccount) - - if includeInner { - for _, inner := range stxnad.ApplyData.EvalDelta.InnerTxns { - getTransactionParticipantsImpl(&inner, includeInner, add) - } - } -} - // getTransactionParticipants returns referenced addresses from the txn and all inner txns func getTransactionParticipants(stxnad *transactions.SignedTxnWithAD, includeInner bool) []basics.Address { const acctsPerTxn = 7 @@ -302,7 +285,7 @@ func getTransactionParticipants(stxnad *transactions.SignedTxnWithAD, includeInn res = append(res, address) } - getTransactionParticipantsImpl(stxnad, includeInner, add) + accounting.GetTransactionParticipants(stxnad, includeInner, add) return res } @@ -318,7 +301,7 @@ func getTransactionParticipants(stxnad *transactions.SignedTxnWithAD, includeInn participants[address] = struct{}{} } - getTransactionParticipantsImpl(stxnad, includeInner, add) + accounting.GetTransactionParticipants(stxnad, includeInner, add) res := make([]basics.Address, 0, len(participants)) for addr := range participants { diff --git a/idb/postgres/internal/writer/writer_test.go b/idb/postgres/internal/writer/writer_test.go index 4b33109d3..51b7c1d8d 100644 --- a/idb/postgres/internal/writer/writer_test.go +++ b/idb/postgres/internal/writer/writer_test.go @@ -410,6 +410,70 @@ func TestWriterTxnParticipationTableBasic(t *testing.T) { } } +func TestWriterTxnParticipationTableAppCallAddresses(t *testing.T) { + db, shutdownFunc := setupPostgres(t) + defer shutdownFunc() + + block := bookkeeping.Block{ + BlockHeader: bookkeeping.BlockHeader{ + Round: basics.Round(2), + GenesisID: test.MakeGenesis().ID(), + GenesisHash: test.GenesisHash, + UpgradeState: bookkeeping.UpgradeState{ + CurrentProtocol: test.Proto, + }, + }, + Payset: make(transactions.Payset, 1), + } + + stxnad := test.MakeCreateAppTxn(test.AccountA) + stxnad.Txn.ApplicationCallTxnFields.Accounts = + []basics.Address{test.AccountB, test.AccountC} + var err error + block.Payset[0], err = block.EncodeSignedTxn(stxnad.SignedTxn, stxnad.ApplyData) + require.NoError(t, err) + + f := func(tx pgx.Tx) error { + w, err := writer.MakeWriter(tx) + require.NoError(t, err) + + err = w.AddBlock(&block, block.Payset, ledgercore.StateDelta{}) + require.NoError(t, err) + + w.Close() + return nil + } + err = pgutil.TxWithRetry(db, serializable, f, nil) + require.NoError(t, err) + + results, err := txnParticipationQuery(db, `SELECT * FROM txn_participation ORDER BY round, intra, addr`) + assert.NoError(t, err) + + expected := []txnParticipationRow{ + { + addr: test.AccountA, + round: 2, + intra: 0, + }, + { + addr: test.AccountB, + round: 2, + intra: 0, + }, + { + addr: test.AccountC, + round: 2, + intra: 0, + }, + } + + // Verify expected participation + assert.Len(t, results, len(expected)) + for i := range results { + assert.Equal(t, expected[i], results[i]) + } +} + // Create a new account and then delete it. func TestWriterAccountTableBasic(t *testing.T) { db, shutdownFunc := setupPostgres(t) diff --git a/idb/postgres/postgres.go b/idb/postgres/postgres.go index d5e03a861..00ee9ecc4 100644 --- a/idb/postgres/postgres.go +++ b/idb/postgres/postgres.go @@ -24,6 +24,7 @@ import ( "github.com/jackc/pgx/v4/pgxpool" log "github.com/sirupsen/logrus" + "github.com/algorand/indexer/accounting" models "github.com/algorand/indexer/api/generated/v2" "github.com/algorand/indexer/idb" "github.com/algorand/indexer/idb/migration" @@ -158,20 +159,6 @@ func (db *IndexerDb) init(opts idb.IndexerDbOptions) (chan struct{}, error) { return db.runAvailableMigrations() } -// Add addresses referenced in `txn` to `out`. -func getTxnAddresses(txn *transactions.Transaction, out map[basics.Address]struct{}) { - out[txn.Sender] = struct{}{} - out[txn.Receiver] = struct{}{} - out[txn.CloseRemainderTo] = struct{}{} - out[txn.AssetSender] = struct{}{} - out[txn.AssetReceiver] = struct{}{} - out[txn.AssetCloseTo] = struct{}{} - out[txn.FreezeAccount] = struct{}{} - for _, address := range txn.ApplicationCallTxnFields.Accounts { - out[address] = struct{}{} - } -} - // Returns all addresses referenced in `block`. func getBlockAddresses(block *bookkeeping.Block) map[basics.Address]struct{} { // Reserve a reasonable memory size for the map. @@ -180,7 +167,10 @@ func getBlockAddresses(block *bookkeeping.Block) map[basics.Address]struct{} { res[block.FeeSink] = struct{}{} res[block.RewardsPool] = struct{}{} for _, stib := range block.Payset { - getTxnAddresses(&stib.Txn, res) + addFunc := func(address basics.Address) { + res[address] = struct{}{} + } + accounting.GetTransactionParticipants(&stib.SignedTxnWithAD, true, addFunc) } return res