diff --git a/go.mod b/go.mod index d988d82248..9987c10d67 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 + github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/mattn/go-sqlite3 v1.14.16 github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 2a2657a0c5..a867591d1b 100644 --- a/go.sum +++ b/go.sum @@ -318,6 +318,8 @@ github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= diff --git a/go/ethadapter/geth_rpc_client.go b/go/ethadapter/geth_rpc_client.go index 305bf83a68..20c79c2cc1 100644 --- a/go/ethadapter/geth_rpc_client.go +++ b/go/ethadapter/geth_rpc_client.go @@ -8,21 +8,19 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" gethlog "github.com/ethereum/go-ethereum/log" - "github.com/ten-protocol/go-ten/contracts/generated/ManagementContract" - "github.com/ten-protocol/go-ten/go/common/retry" - - "github.com/ten-protocol/go-ten/go/common/log" + lru "github.com/hashicorp/golang-lru/v2" + "github.com/ten-protocol/go-ten/contracts/generated/ManagementContract" "github.com/ten-protocol/go-ten/go/common" - - "github.com/ethereum/go-ethereum" - - gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" + "github.com/ten-protocol/go-ten/go/common/log" + "github.com/ten-protocol/go-ten/go/common/retry" ) const ( @@ -30,15 +28,17 @@ const ( connRetryInterval = 500 * time.Millisecond _maxRetryPriceIncreases = 5 _retryPriceMultiplier = 1.2 + _defaultBlockCacheSize = 51 // enough for 50 request batch size and one for previous block ) // gethRPCClient implements the EthClient interface and allows connection to a real ethereum node type gethRPCClient struct { - client *ethclient.Client // the underlying eth rpc client - l2ID gethcommon.Address // the address of the Obscuro node this client is dedicated to - timeout time.Duration // the timeout for connecting to, or communicating with, the L1 node - logger gethlog.Logger - rpcURL string + client *ethclient.Client // the underlying eth rpc client + l2ID gethcommon.Address // the address of the Obscuro node this client is dedicated to + timeout time.Duration // the timeout for connecting to, or communicating with, the L1 node + logger gethlog.Logger + rpcURL string + blockCache *lru.Cache[gethcommon.Hash, *types.Block] } // NewEthClientFromURL instantiates a new ethadapter.EthClient that connects to an ethereum node @@ -49,12 +49,20 @@ func NewEthClientFromURL(rpcURL string, timeout time.Duration, l2ID gethcommon.A } logger.Trace(fmt.Sprintf("Initialized eth node connection - addr: %s", rpcURL)) + + // cache recent blocks to avoid re-fetching them (they are often re-used for checking for forks etc.) + blkCache, err := lru.New[gethcommon.Hash, *types.Block](_defaultBlockCacheSize) + if err != nil { + return nil, fmt.Errorf("unable to initialize block cache - %w", err) + } + return &gethRPCClient{ - client: client, - l2ID: l2ID, - timeout: timeout, - logger: logger, - rpcURL: rpcURL, + client: client, + l2ID: l2ID, + timeout: timeout, + logger: logger, + rpcURL: rpcURL, + blockCache: blkCache, }, nil } @@ -181,10 +189,21 @@ func (e *gethRPCClient) BlockByNumber(n *big.Int) (*types.Block, error) { } func (e *gethRPCClient) BlockByHash(hash gethcommon.Hash) (*types.Block, error) { + block, found := e.blockCache.Get(hash) + if found { + return block, nil + } + + // not in cache, fetch from RPC ctx, cancel := context.WithTimeout(context.Background(), e.timeout) defer cancel() - return e.client.BlockByHash(ctx, hash) + block, err := e.client.BlockByHash(ctx, hash) + if err != nil { + return nil, err + } + e.blockCache.Add(hash, block) + return block, nil } func (e *gethRPCClient) CallContract(msg ethereum.CallMsg) ([]byte, error) {