diff --git a/go.mod b/go.mod index e621cc1..bb3a998 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/coinbase/rosetta-sdk-go v0.8.6 github.com/coinbase/rosetta-sdk-go/types v1.0.0 github.com/ethereum/go-ethereum v1.13.8 + github.com/hashicorp/golang-lru v0.5.1 github.com/neilotoole/errgroup v0.1.6 github.com/stretchr/testify v1.8.4 golang.org/x/crypto v0.17.0 diff --git a/go.sum b/go.sum index e8ba6cf..8cfcdd7 100644 --- a/go.sum +++ b/go.sum @@ -257,6 +257,7 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= diff --git a/services/block_service.go b/services/block_service.go index 140b132..bb2bc3e 100644 --- a/services/block_service.go +++ b/services/block_service.go @@ -19,11 +19,13 @@ import ( "encoding/json" "errors" "fmt" + "log" "math" "math/big" goEthereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common/hexutil" + lru "github.com/hashicorp/golang-lru" client "github.com/coinbase/rosetta-geth-sdk/client" construction "github.com/coinbase/rosetta-geth-sdk/services/construction" @@ -38,13 +40,17 @@ import ( ) const ( + // LRUCacheSize determines how many contract currencies we cache + LRUCacheSize = 100 + OpenEthereumTrace = iota // == 2 ) // BlockAPIService implements the server.BlockAPIServicer interface. type BlockAPIService struct { - config *configuration.Configuration - client construction.Client + config *configuration.Configuration + client construction.Client + currencyCache *lru.Cache } // NewBlockAPIService creates a new instance of a BlockAPIService. @@ -52,9 +58,15 @@ func NewBlockAPIService( cfg *configuration.Configuration, client construction.Client, ) *BlockAPIService { + currencyCache, err := lru.New(LRUCacheSize) + if err != nil { + log.Fatalln(err) + } + return &BlockAPIService{ - config: cfg, - client: client, + config: cfg, + client: client, + currencyCache: currencyCache, } } @@ -67,9 +79,6 @@ func (s *BlockAPIService) populateTransactions( rosettaCfg := s.client.GetRosettaConfig() transactions := make([]*RosettaTypes.Transaction, 0) - // Create a shared currency map for all transactions in this block - contractCurrencyMap := make(map[string]*client.ContractCurrency) - if rosettaCfg.SupportRewardTx { // Compute reward transaction (block + uncle reward) rewardTx := s.client.BlockRewardTransaction( @@ -85,7 +94,7 @@ func (s *BlockAPIService) populateTransactions( // Bridge tx is already handled in PopulateCrossChainTransactions flow continue } - transaction, err := s.PopulateTransaction(ctx, tx, contractCurrencyMap) + transaction, err := s.PopulateTransaction(ctx, tx) if err != nil { return nil, fmt.Errorf("cannot parse %s: %w", tx.TxHash, err) } @@ -98,7 +107,6 @@ func (s *BlockAPIService) populateTransactions( func (s *BlockAPIService) PopulateTransaction( ctx context.Context, tx *client.LoadedTransaction, - contractCurrencyMap map[string]*client.ContractCurrency, ) (*RosettaTypes.Transaction, error) { ops, err := s.client.ParseOps(tx) if err != nil { @@ -124,13 +132,13 @@ func (s *BlockAPIService) PopulateTransaction( var currency *client.ContractCurrency var err error - if cachedCurrency, found := contractCurrencyMap[addressStr]; found { - currency = cachedCurrency + if cachedCurrency, found := s.currencyCache.Get(addressStr); found { + currency = cachedCurrency.(*client.ContractCurrency) } else { if currency, err = s.client.GetContractCurrency(log.Address, true); err != nil { return nil, err } - contractCurrencyMap[addressStr] = currency + s.currencyCache.Add(addressStr, currency) } if currency.Symbol == client.UnknownERC20Symbol && !s.config.RosettaCfg.IndexUnknownTokens { @@ -476,10 +484,7 @@ func (s *BlockAPIService) BlockTransaction( loadedTx.FeeBurned = nil } - // Create a shared currency map for all logs for this transaction - contractCurrencyMap := make(map[string]*client.ContractCurrency) - - transaction, err := s.PopulateTransaction(ctx, loadedTx, contractCurrencyMap) + transaction, err := s.PopulateTransaction(ctx, loadedTx) if err != nil { return nil, AssetTypes.WrapErr(AssetTypes.ErrInternalError, fmt.Errorf("unable to populate tx: %w", err)) }