From 4420f2304f13c49257fea08c91a7bd3d988183de Mon Sep 17 00:00:00 2001 From: Tolik Zinovyev Date: Tue, 21 Sep 2021 18:11:47 -0400 Subject: [PATCH] Cache creators in cow base. --- ledger/appcow.go | 1 + ledger/eval.go | 30 ++++++++++++++++++- ledger/evalIndexer.go | 6 ---- ledger/eval_test.go | 69 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 7 deletions(-) diff --git a/ledger/appcow.go b/ledger/appcow.go index 6da14e68df..64e718c7db 100644 --- a/ledger/appcow.go +++ b/ledger/appcow.go @@ -463,6 +463,7 @@ func MakeDebugBalances(l ledgerForCowBase, round basics.Round, proto protocol.Co rnd: round - 1, proto: config.Consensus[proto], accounts: make(map[basics.Address]basics.AccountData), + creators: make(map[creatable]FoundAddress), } hdr := bookkeeping.BlockHeader{ diff --git a/ledger/eval.go b/ledger/eval.go index 765198b3c2..091a1d6ba2 100644 --- a/ledger/eval.go +++ b/ledger/eval.go @@ -54,6 +54,17 @@ const maxPaysetHint = 20000 // transaction group. const asyncAccountLoadingThreadCount = 4 +type creatable struct { + cindex basics.CreatableIndex + ctype basics.CreatableType +} + +// FoundAddress is a wrapper for an address and a boolean. +type FoundAddress struct { + Address basics.Address + Exists bool +} + type roundCowBase struct { l ledgerForCowBase @@ -79,10 +90,26 @@ type roundCowBase struct { // are beyond the scope of this cache. // The account data store here is always the account data without the rewards. accounts map[basics.Address]basics.AccountData + + // Similar cache for asset/app creators. + creators map[creatable]FoundAddress } func (x *roundCowBase) getCreator(cidx basics.CreatableIndex, ctype basics.CreatableType) (basics.Address, bool, error) { - return x.l.GetCreatorForRound(x.rnd, cidx, ctype) + creatable := creatable{cindex: cidx, ctype: ctype} + + if foundAddress, ok := x.creators[creatable]; ok { + return foundAddress.Address, foundAddress.Exists, nil + } + + address, exists, err := x.l.GetCreatorForRound(x.rnd, cidx, ctype) + if err != nil { + return basics.Address{}, false, fmt.Errorf( + "roundCowBase.getCreator() cidx: %d ctype: %v err: %w", cidx, ctype, err) + } + + x.creators[creatable] = FoundAddress{Address: address, Exists: exists} + return address, exists, nil } // lookup returns the non-rewarded account data for the provided account address. It uses the internal per-round cache @@ -412,6 +439,7 @@ func startEvaluator(l ledgerForEvaluator, hdr bookkeeping.BlockHeader, proto con txnCount: prevHeader.TxnCounter, proto: proto, accounts: make(map[basics.Address]basics.AccountData), + creators: make(map[creatable]FoundAddress), } eval := &BlockEvaluator{ diff --git a/ledger/evalIndexer.go b/ledger/evalIndexer.go index c139c39add..8fa69fc0b0 100644 --- a/ledger/evalIndexer.go +++ b/ledger/evalIndexer.go @@ -28,12 +28,6 @@ import ( "github.com/algorand/go-algorand/ledger/ledgercore" ) -// FoundAddress is a wrapper for an address and a boolean. -type FoundAddress struct { - Address basics.Address - Exists bool -} - // A ledger interface that Indexer implements. This is a simplified version of the // ledgerForEvaluator interface. Certain functions that the evaluator doesn't use // in the trusting mode are excluded, and the present functions only request data diff --git a/ledger/eval_test.go b/ledger/eval_test.go index c6e8ed957f..7bc754edcf 100644 --- a/ledger/eval_test.go +++ b/ledger/eval_test.go @@ -1732,3 +1732,72 @@ func TestAppInsMinBalance(t *testing.T) { vb := l.endBlock(t, eval) assert.Len(t, vb.delta.ModifiedAppLocalStates, 50) } + +type getCreatorForRoundResult struct { + address basics.Address + exists bool +} + +type testCowBaseLedger struct { + creators []getCreatorForRoundResult +} + +func (l *testCowBaseLedger) BlockHdr(basics.Round) (bookkeeping.BlockHeader, error) { + return bookkeeping.BlockHeader{}, errors.New("not implemented") +} + +func (l *testCowBaseLedger) CheckDup(config.ConsensusParams, basics.Round, basics.Round, basics.Round, transactions.Txid, TxLease) error { + return errors.New("not implemented") +} + +func (l *testCowBaseLedger) LookupWithoutRewards(basics.Round, basics.Address) (basics.AccountData, basics.Round, error) { + return basics.AccountData{}, basics.Round(0), errors.New("not implemented") +} + +func (l *testCowBaseLedger) GetCreatorForRound(_ basics.Round, cindex basics.CreatableIndex, ctype basics.CreatableType) (basics.Address, bool, error) { + res := l.creators[0] + l.creators = l.creators[1:] + return res.address, res.exists, nil +} + +func TestCowBaseCreatorsCache(t *testing.T) { + partitiontest.PartitionTest(t) + + addresses := make([]basics.Address, 3) + for i := 0; i < len(addresses); i++ { + _, err := rand.Read(addresses[i][:]) + require.NoError(t, err) + } + + creators := []getCreatorForRoundResult{ + {address: addresses[0], exists: true}, + {address: basics.Address{}, exists: false}, + {address: addresses[1], exists: true}, + {address: basics.Address{}, exists: false}, + } + l := testCowBaseLedger{ + creators: creators, + } + + base := roundCowBase{ + l: &l, + creators: map[creatable]FoundAddress{}, + } + + cindex := []basics.CreatableIndex{9, 10, 9, 10} + ctype := []basics.CreatableType{ + basics.AssetCreatable, + basics.AssetCreatable, + basics.AppCreatable, + basics.AppCreatable, + } + for i := 0; i < 2; i++ { + for j, expected := range creators { + address, exists, err := base.getCreator(cindex[j], ctype[j]) + require.NoError(t, err) + + assert.Equal(t, expected.address, address) + assert.Equal(t, expected.exists, exists) + } + } +}