@@ -20,9 +20,12 @@ import (
2020 "context"
2121 "errors"
2222 "fmt"
23+ "io"
2324 "math/big"
2425 "os"
26+ "runtime"
2527 "strings"
28+ "sync"
2629 "time"
2730
2831 "github.com/davecgh/go-spew/spew"
@@ -42,6 +45,7 @@ import (
4245 "github.com/ethereum/go-ethereum/crypto"
4346 "github.com/ethereum/go-ethereum/crypto/vrf"
4447 "github.com/ethereum/go-ethereum/eth/tracers/logger"
48+ "github.com/ethereum/go-ethereum/ethdb"
4549 "github.com/ethereum/go-ethereum/log"
4650 "github.com/ethereum/go-ethereum/p2p"
4751 "github.com/ethereum/go-ethereum/params"
@@ -50,6 +54,29 @@ import (
5054 "github.com/tyler-smith/go-bip39"
5155)
5256
57+ var apiRequestsCache ethdb.Database
58+ var apiRequestsThrottle chan struct {}
59+ var apiRequestsTokens chan struct {}
60+
61+ func apiRequestsEnter () {
62+ if len (apiRequestsThrottle ) >= int (params .MaxPublicRequests ) {
63+ pc , _ , _ , _ := runtime .Caller (1 )
64+ var name string
65+ parts := strings .Split (runtime .FuncForPC (pc ).Name (), "." )
66+ if len (parts ) > 0 {
67+ name = parts [len (parts )- 1 ]
68+ } else {
69+ name = runtime .FuncForPC (pc ).Name ()
70+ }
71+ log .Warn ("Too many API requests" , "func" , name , "count" , len (apiRequestsThrottle ))
72+ }
73+ apiRequestsThrottle <- struct {}{}
74+ }
75+
76+ func apiRequestsLeave () {
77+ <- apiRequestsThrottle
78+ }
79+
5380// PublicEthereumAPI provides an API to access Ethereum related information.
5481// It offers only methods that operate on public data that is freely available to anyone.
5582type PublicEthereumAPI struct {
@@ -681,6 +708,20 @@ type PublicBlockChainAPI struct {
681708
682709// NewPublicBlockChainAPI creates a new Ethereum blockchain API.
683710func NewPublicBlockChainAPI (b Backend ) * PublicBlockChainAPI {
711+ if len (params .PublicRequestsCacheLocation ) > 0 {
712+ var err error
713+ apiRequestsCache , err = apiCacheOpen (params .PublicRequestsCacheLocation )
714+ if err != nil {
715+ panic (err )
716+ }
717+ }
718+ apiRequestsThrottle = make (chan struct {}, params .MaxPublicRequests )
719+ tokens := runtime .NumCPU () * 8 / 10
720+ if tokens < 4 {
721+ tokens = 4
722+ }
723+ apiRequestsTokens = make (chan struct {}, tokens )
724+
684725 return & PublicBlockChainAPI {b }
685726}
686727
@@ -701,6 +742,21 @@ func (s *PublicBlockChainAPI) BlockNumber() hexutil.Uint64 {
701742
702743// GetBlockReceipts returns all the transaction receipts for the given block hash.
703744func (s * PublicBlockChainAPI ) GetReceiptsByHash (ctx context.Context , blockHash common.Hash ) ([]map [string ]interface {}, error ) {
745+ apiRequestsEnter ()
746+ defer apiRequestsLeave ()
747+
748+ select {
749+ case <- ctx .Done ():
750+ return nil , io .EOF
751+ default :
752+ }
753+
754+ if apiRequestsCache != nil {
755+ if fields , err := apiCacheGetReceipts (apiRequestsCache , blockHash .Bytes ()); err == nil {
756+ log .Debug ("API Cache" , "found receipts" , blockHash )
757+ return fields , nil
758+ }
759+ }
704760
705761 block , err1 := s .b .BlockByHash (ctx , blockHash )
706762 if block == nil && err1 == nil {
@@ -723,6 +779,11 @@ func (s *PublicBlockChainAPI) GetReceiptsByHash(ctx context.Context, blockHash c
723779 fieldsList := make ([]map [string ]interface {}, 0 , len (receipts ))
724780
725781 for index , receipt := range receipts {
782+ select {
783+ case <- ctx .Done ():
784+ return nil , io .EOF
785+ default :
786+ }
726787
727788 bigblock := new (big.Int ).SetUint64 (block .NumberU64 ())
728789 signer := types .MakeSigner (s .b .ChainConfig (), bigblock )
@@ -770,6 +831,9 @@ func (s *PublicBlockChainAPI) GetReceiptsByHash(ctx context.Context, blockHash c
770831
771832 fieldsList = append (fieldsList , fields )
772833 }
834+ if apiRequestsCache != nil {
835+ apiCachePutReceipts (apiRequestsCache , blockHash .Bytes (), fieldsList )
836+ }
773837 return fieldsList , nil
774838}
775839
@@ -884,6 +948,9 @@ func (s *PublicBlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.H
884948// - When fullTx is true all transactions in the block are returned, otherwise
885949// only the transaction hash is returned.
886950func (s * PublicBlockChainAPI ) GetBlockByNumber (ctx context.Context , number rpc.BlockNumber , fullTx bool ) (map [string ]interface {}, error ) {
951+ apiRequestsEnter ()
952+ defer apiRequestsLeave ()
953+
887954 block , err := s .b .BlockByNumber (ctx , number )
888955 if block != nil && err == nil {
889956 response , err := s .rpcMarshalBlock (ctx , block , true , fullTx )
@@ -901,6 +968,9 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.B
901968// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full
902969// detail, otherwise only the transaction hash is returned.
903970func (s * PublicBlockChainAPI ) GetBlockByHash (ctx context.Context , hash common.Hash , fullTx bool ) (map [string ]interface {}, error ) {
971+ apiRequestsEnter ()
972+ defer apiRequestsLeave ()
973+
904974 block , err := s .b .BlockByHash (ctx , hash )
905975 if block != nil {
906976 return s .rpcMarshalBlock (ctx , block , true , fullTx )
@@ -910,6 +980,9 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Ha
910980
911981// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index.
912982func (s * PublicBlockChainAPI ) GetUncleByBlockNumberAndIndex (ctx context.Context , blockNr rpc.BlockNumber , index hexutil.Uint ) (map [string ]interface {}, error ) {
983+ apiRequestsEnter ()
984+ defer apiRequestsLeave ()
985+
913986 block , err := s .b .BlockByNumber (ctx , blockNr )
914987 if block != nil {
915988 uncles := block .Uncles ()
@@ -925,6 +998,9 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context,
925998
926999// GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index.
9271000func (s * PublicBlockChainAPI ) GetUncleByBlockHashAndIndex (ctx context.Context , blockHash common.Hash , index hexutil.Uint ) (map [string ]interface {}, error ) {
1001+ apiRequestsEnter ()
1002+ defer apiRequestsLeave ()
1003+
9281004 block , err := s .b .BlockByHash (ctx , blockHash )
9291005 if block != nil {
9301006 uncles := block .Uncles ()
@@ -940,6 +1016,9 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, b
9401016
9411017// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number
9421018func (s * PublicBlockChainAPI ) GetUncleCountByBlockNumber (ctx context.Context , blockNr rpc.BlockNumber ) * hexutil.Uint {
1019+ apiRequestsEnter ()
1020+ defer apiRequestsLeave ()
1021+
9431022 if block , _ := s .b .BlockByNumber (ctx , blockNr ); block != nil {
9441023 n := hexutil .Uint (len (block .Uncles ()))
9451024 return & n
@@ -949,6 +1028,9 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, bl
9491028
9501029// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash
9511030func (s * PublicBlockChainAPI ) GetUncleCountByBlockHash (ctx context.Context , blockHash common.Hash ) * hexutil.Uint {
1031+ apiRequestsEnter ()
1032+ defer apiRequestsLeave ()
1033+
9521034 if block , _ := s .b .BlockByHash (ctx , blockHash ); block != nil {
9531035 n := hexutil .Uint (len (block .Uncles ()))
9541036 return & n
@@ -1299,7 +1381,20 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
12991381// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
13001382// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
13011383// transaction hashes.
1302- func RPCMarshalBlock (block * types.Block , inclTx bool , fullTx bool , config * params.ChainConfig ) (map [string ]interface {}, error ) {
1384+ func RPCMarshalBlock (ctx context.Context , block * types.Block , inclTx bool , fullTx bool , config * params.ChainConfig ) (map [string ]interface {}, error ) {
1385+ select {
1386+ case <- ctx .Done ():
1387+ return nil , io .EOF
1388+ default :
1389+ }
1390+
1391+ if fullTx && apiRequestsCache != nil {
1392+ if fields , err := apiCacheGetBlock (apiRequestsCache , block .Hash ().Bytes ()); err == nil {
1393+ log .Debug ("API Cache" , "found block" , block .Number ())
1394+ return fields , nil
1395+ }
1396+ }
1397+
13031398 fields := RPCMarshalHeader (block .Header ())
13041399 fields ["size" ] = hexutil .Uint64 (block .Size ())
13051400
@@ -1314,11 +1409,33 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param
13141409 }
13151410 txs := block .Transactions ()
13161411 transactions := make ([]interface {}, len (txs ))
1412+ var wg sync.WaitGroup
13171413 var err error
13181414 for i , tx := range txs {
1319- if transactions [i ], err = formatTx (tx ); err != nil {
1320- return nil , err
1321- }
1415+ wg .Add (1 )
1416+ go func (ii int , itx * types.Transaction ) {
1417+ apiRequestsTokens <- struct {}{}
1418+ defer func () {
1419+ wg .Done ()
1420+ <- apiRequestsTokens
1421+ }()
1422+
1423+ select {
1424+ case <- ctx .Done ():
1425+ err = io .EOF
1426+ return
1427+ default :
1428+ }
1429+ var err2 error
1430+ transactions [ii ], err2 = formatTx (itx )
1431+ if err2 != nil {
1432+ err = err2
1433+ }
1434+ }(i , tx )
1435+ }
1436+ wg .Wait ()
1437+ if err != nil {
1438+ return nil , err
13221439 }
13231440 fields ["transactions" ] = transactions
13241441 }
@@ -1329,6 +1446,10 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param
13291446 }
13301447 fields ["uncles" ] = uncleHashes
13311448
1449+ if fullTx && apiRequestsCache != nil {
1450+ apiCachePutBlock (apiRequestsCache , block .Hash ().Bytes (), fields )
1451+ }
1452+
13321453 return fields , nil
13331454}
13341455
@@ -1343,7 +1464,7 @@ func (s *PublicBlockChainAPI) rpcMarshalHeader(ctx context.Context, header *type
13431464// rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field, which requires
13441465// a `PublicBlockchainAPI`.
13451466func (s * PublicBlockChainAPI ) rpcMarshalBlock (ctx context.Context , b * types.Block , inclTx bool , fullTx bool ) (map [string ]interface {}, error ) {
1346- fields , err := RPCMarshalBlock (b , inclTx , fullTx , s .b .ChainConfig ())
1467+ fields , err := RPCMarshalBlock (ctx , b , inclTx , fullTx , s .b .ChainConfig ())
13471468 if err != nil {
13481469 return nil , err
13491470 }
0 commit comments