summaryrefslogtreecommitdiff
path: root/ledger/evalindexer.go
diff options
context:
space:
mode:
Diffstat (limited to 'ledger/evalindexer.go')
-rw-r--r--ledger/evalindexer.go194
1 files changed, 194 insertions, 0 deletions
diff --git a/ledger/evalindexer.go b/ledger/evalindexer.go
new file mode 100644
index 000000000..251e7c1f6
--- /dev/null
+++ b/ledger/evalindexer.go
@@ -0,0 +1,194 @@
+// Copyright (C) 2019-2021 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
+
+package ledger
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/algorand/go-algorand/config"
+ "github.com/algorand/go-algorand/crypto"
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/data/bookkeeping"
+ "github.com/algorand/go-algorand/data/transactions"
+ "github.com/algorand/go-algorand/ledger/internal"
+ "github.com/algorand/go-algorand/ledger/ledgercore"
+)
+
+// 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
+// at the latest round.
+type indexerLedgerForEval interface {
+ LatestBlockHdr() (bookkeeping.BlockHeader, error)
+ // The value of the returned map is nil iff the account was not found.
+ LookupWithoutRewards(map[basics.Address]struct{}) (map[basics.Address]*basics.AccountData, error)
+ GetAssetCreator(map[basics.AssetIndex]struct{}) (map[basics.AssetIndex]FoundAddress, error)
+ GetAppCreator(map[basics.AppIndex]struct{}) (map[basics.AppIndex]FoundAddress, error)
+ LatestTotals() (ledgercore.AccountTotals, error)
+}
+
+// FoundAddress is a wrapper for an address and a boolean.
+type FoundAddress struct {
+ Address basics.Address
+ Exists bool
+}
+
+// EvalForIndexerResources contains resources preloaded from the Indexer database.
+// Indexer is able to do the preloading more efficiently than the evaluator loading
+// resources one by one.
+type EvalForIndexerResources struct {
+ // The map value is nil iff the account does not exist. The account data is owned here.
+ Accounts map[basics.Address]*basics.AccountData
+ Creators map[Creatable]FoundAddress
+}
+
+// Creatable represent a single creatable object.
+type Creatable struct {
+ Index basics.CreatableIndex
+ Type basics.CreatableType
+}
+
+// Converter between indexerLedgerForEval and ledgerForEvaluator interfaces.
+type indexerLedgerConnector struct {
+ il indexerLedgerForEval
+ genesisHash crypto.Digest
+ latestRound basics.Round
+ roundResources EvalForIndexerResources
+}
+
+// BlockHdr is part of LedgerForEvaluator interface.
+func (l indexerLedgerConnector) BlockHdr(round basics.Round) (bookkeeping.BlockHeader, error) {
+ if round != l.latestRound {
+ return bookkeeping.BlockHeader{}, fmt.Errorf(
+ "BlockHdr() evaluator called this function for the wrong round %d, "+
+ "latest round is %d",
+ round, l.latestRound)
+ }
+ return l.il.LatestBlockHdr()
+}
+
+// CheckDup is part of LedgerForEvaluator interface.
+func (l indexerLedgerConnector) CheckDup(config.ConsensusParams, basics.Round, basics.Round, basics.Round, transactions.Txid, ledgercore.Txlease) error {
+ // This function is not used by evaluator.
+ return errors.New("CheckDup() not implemented")
+}
+
+// LookupWithoutRewards is part of LedgerForEvaluator interface.
+func (l indexerLedgerConnector) LookupWithoutRewards(round basics.Round, address basics.Address) (basics.AccountData, basics.Round, error) {
+ // check to see if the account data in the cache.
+ if pad, has := l.roundResources.Accounts[address]; has {
+ if pad == nil {
+ return basics.AccountData{}, round, nil
+ }
+ return *pad, round, nil
+ }
+
+ accountDataMap, err :=
+ l.il.LookupWithoutRewards(map[basics.Address]struct{}{address: {}})
+ if err != nil {
+ return basics.AccountData{}, basics.Round(0), err
+ }
+
+ accountData := accountDataMap[address]
+ if accountData == nil {
+ return basics.AccountData{}, round, nil
+ }
+ return *accountData, round, nil
+}
+
+// GetCreatorForRound is part of LedgerForEvaluator interface.
+func (l indexerLedgerConnector) GetCreatorForRound(_ basics.Round, cindex basics.CreatableIndex, ctype basics.CreatableType) (basics.Address, bool, error) {
+ var foundAddress FoundAddress
+ var has bool
+ // check to see if the account data in the cache.
+ if foundAddress, has = l.roundResources.Creators[Creatable{Index: cindex, Type: ctype}]; has {
+ return foundAddress.Address, foundAddress.Exists, nil
+ }
+
+ switch ctype {
+ case basics.AssetCreatable:
+ foundAddresses, err :=
+ l.il.GetAssetCreator(map[basics.AssetIndex]struct{}{basics.AssetIndex(cindex): {}})
+ if err != nil {
+ return basics.Address{}, false, err
+ }
+ foundAddress = foundAddresses[basics.AssetIndex(cindex)]
+ case basics.AppCreatable:
+ foundAddresses, err :=
+ l.il.GetAppCreator(map[basics.AppIndex]struct{}{basics.AppIndex(cindex): {}})
+ if err != nil {
+ return basics.Address{}, false, err
+ }
+ foundAddress = foundAddresses[basics.AppIndex(cindex)]
+ default:
+ return basics.Address{}, false, fmt.Errorf("unknown creatable type %v", ctype)
+ }
+
+ return foundAddress.Address, foundAddress.Exists, nil
+}
+
+// GenesisHash is part of LedgerForEvaluator interface.
+func (l indexerLedgerConnector) GenesisHash() crypto.Digest {
+ return l.genesisHash
+}
+
+// Totals is part of LedgerForEvaluator interface.
+func (l indexerLedgerConnector) LatestTotals() (rnd basics.Round, totals ledgercore.AccountTotals, err error) {
+ totals, err = l.il.LatestTotals()
+ rnd = l.latestRound
+ return
+}
+
+// CompactCertVoters is part of LedgerForEvaluator interface.
+func (l indexerLedgerConnector) CompactCertVoters(_ basics.Round) (*ledgercore.VotersForRound, error) {
+ // This function is not used by evaluator.
+ return nil, errors.New("CompactCertVoters() not implemented")
+}
+
+func makeIndexerLedgerConnector(il indexerLedgerForEval, genesisHash crypto.Digest, latestRound basics.Round, roundResources EvalForIndexerResources) indexerLedgerConnector {
+ return indexerLedgerConnector{
+ il: il,
+ genesisHash: genesisHash,
+ latestRound: latestRound,
+ roundResources: roundResources,
+ }
+}
+
+// EvalForIndexer 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
+// close amount for each transaction even when the real consensus parameters do not
+// support it.
+func EvalForIndexer(il indexerLedgerForEval, block *bookkeeping.Block, proto config.ConsensusParams, resources EvalForIndexerResources) (ledgercore.StateDelta, []transactions.SignedTxnInBlock, error) {
+ ilc := makeIndexerLedgerConnector(il, block.GenesisHash(), block.Round()-1, resources)
+
+ eval, err := internal.StartEvaluator(
+ ilc, block.BlockHeader,
+ internal.EvaluatorOptions{
+ PaysetHint: len(block.Payset),
+ ProtoParams: &proto,
+ Generate: false,
+ Validate: false,
+ })
+ if err != nil {
+ return ledgercore.StateDelta{}, []transactions.SignedTxnInBlock{},
+ fmt.Errorf("EvalForIndexer() err: %w", err)
+ }
+
+ return eval.ProcessBlockForIndexer(block)
+}