Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce cache layer for fee denom and decimals #110

Merged
merged 5 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 58 additions & 3 deletions jsonrpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package backend

import (
"context"
"fmt"
"sync"
"time"

Expand Down Expand Up @@ -39,6 +40,10 @@ type JSONRPCBackend struct {
txLookupCache *lru.Cache[common.Hash, *rpctypes.RPCTransaction]
receiptCache *lru.Cache[common.Hash, *coretypes.Receipt]

// fee cache
feeDenom string
feeDecimals uint8
beer-1 marked this conversation as resolved.
Show resolved Hide resolved

mut sync.Mutex // mutex for accMuts
accMuts map[string]*AccMut

Expand All @@ -65,6 +70,7 @@ const (

// NewJSONRPCBackend creates a new JSONRPCBackend instance
func NewJSONRPCBackend(
ctx context.Context,
app *app.MinitiaApp,
logger log.Logger,
svrCtx *server.Context,
Expand All @@ -86,8 +92,7 @@ func NewJSONRPCBackend(
return nil, err
}

ctx := context.Background()
return &JSONRPCBackend{
b := &JSONRPCBackend{
app: app,
logger: logger,

Expand All @@ -113,7 +118,57 @@ func NewJSONRPCBackend(
svrCtx: svrCtx,
clientCtx: clientCtx,
cfg: cfg,
}, nil
}

// start fee fetcher
go b.feeFetcher()

beer-1 marked this conversation as resolved.
Show resolved Hide resolved
return b, nil
}

func (b *JSONRPCBackend) feeFetcher() {
fetcher := func() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("feeFetcher panic: %v", r)
}
}()

queryCtx, err := b.getQueryCtx()
if err != nil {
return err
}

params, err := b.app.EVMKeeper.Params.Get(queryCtx)
if err != nil {
return err
}

feeDenom := params.FeeDenom
decimals, err := b.app.EVMKeeper.ERC20Keeper().GetDecimals(queryCtx, feeDenom)
if err != nil {
return err
}

b.feeDenom = feeDenom
b.feeDecimals = decimals

return nil
}

ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()

for {
select {
case <-ticker.C:
if err := fetcher(); err != nil {
b.logger.Error("failed to fetch fee", "err", err)
}
case <-b.ctx.Done():
return
}
}
beer-1 marked this conversation as resolved.
Show resolved Hide resolved
}

type AccMut struct {
Expand Down
10 changes: 5 additions & 5 deletions jsonrpc/backend/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ func (b *JSONRPCBackend) GetBalance(address common.Address, blockNrOrHash rpc.Bl
return nil, err
}

feeDenom, decimals, err := b.feeDenomWithDecimals()
if err != nil {
return nil, err
// jsonrpc is not ready for querying
if b.feeDenom == "" {
return nil, errors.New("jsonrpc is not ready")
}

balance, err := b.app.EVMKeeper.ERC20Keeper().GetBalance(queryCtx, sdk.AccAddress(address[:]), feeDenom)
balance, err := b.app.EVMKeeper.ERC20Keeper().GetBalance(queryCtx, sdk.AccAddress(address[:]), b.feeDenom)
if err != nil {
return nil, err
}

return (*hexutil.Big)(types.ToEthersUint(decimals, balance.BigInt())), nil
return (*hexutil.Big)(types.ToEthersUint(b.feeDecimals, balance.BigInt())), nil
}

func (b *JSONRPCBackend) Call(args rpctypes.TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *rpctypes.StateOverride, blockOverrides *rpctypes.BlockOverrides) (hexutil.Bytes, error) {
Expand Down
53 changes: 10 additions & 43 deletions jsonrpc/backend/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,24 @@ func (b *JSONRPCBackend) EstimateGas(args rpctypes.TransactionArgs, blockNrOrHas
return hexutil.Uint64(0), err
}

_, decimals, err := b.feeDenomWithDecimals()
if err != nil {
return hexutil.Uint64(0), err
// jsonrpc is not ready for querying
if b.feeDenom == "" {
return hexutil.Uint64(0), NewInternalError("jsonrpc is not ready")
}

sdkMsgs := []sdk.Msg{}
if args.To == nil {
sdkMsgs = append(sdkMsgs, &types.MsgCreate{
Sender: sender,
Code: hexutil.Encode(args.GetData()),
Value: math.NewIntFromBigInt(types.FromEthersUnit(decimals, args.Value.ToInt())),
Value: math.NewIntFromBigInt(types.FromEthersUnit(b.feeDecimals, args.Value.ToInt())),
})
} else {
sdkMsgs = append(sdkMsgs, &types.MsgCall{
Sender: sender,
ContractAddr: args.To.Hex(),
Input: hexutil.Encode(args.GetData()),
Value: math.NewIntFromBigInt(types.FromEthersUnit(decimals, args.Value.ToInt())),
Value: math.NewIntFromBigInt(types.FromEthersUnit(b.feeDecimals, args.Value.ToInt())),
})
}

Expand Down Expand Up @@ -90,39 +90,6 @@ func (b *JSONRPCBackend) EstimateGas(args rpctypes.TransactionArgs, blockNrOrHas
return hexutil.Uint64(gasInfo.GasUsed), nil
}

func (b *JSONRPCBackend) feeDenom() (string, error) {
queryCtx, err := b.getQueryCtx()
if err != nil {
return "", err
}

params, err := b.app.EVMKeeper.Params.Get(queryCtx)
if err != nil {
return "", err
}

return params.FeeDenom, nil
}

func (b *JSONRPCBackend) feeDenomWithDecimals() (string, uint8, error) {
feeDenom, err := b.feeDenom()
if err != nil {
return "", 0, err
}

queryCtx, err := b.getQueryCtx()
if err != nil {
return "", 0, err
}

decimals, err := b.app.EVMKeeper.ERC20Keeper().GetDecimals(queryCtx, feeDenom)
if err != nil {
return "", 0, err
}

return feeDenom, decimals, nil
}

func (b *JSONRPCBackend) GasPrice() (*hexutil.Big, error) {
queryCtx, err := b.getQueryCtx()
if err != nil {
Expand All @@ -134,17 +101,17 @@ func (b *JSONRPCBackend) GasPrice() (*hexutil.Big, error) {
return nil, err
}

feeDenom, decimals, err := b.feeDenomWithDecimals()
if err != nil {
return nil, err
// jsonrpc is not ready for querying
if b.feeDenom == "" {
return nil, NewInternalError("jsonrpc is not ready")
beer-1 marked this conversation as resolved.
Show resolved Hide resolved
}

// multiply by 1e9 to prevent decimal drops
gasPrice := params.MinGasPrices.AmountOf(feeDenom).
gasPrice := params.MinGasPrices.AmountOf(b.feeDenom).
MulTruncate(math.LegacyNewDec(1e9)).
TruncateInt().BigInt()

return (*hexutil.Big)(types.ToEthersUint(decimals+9, gasPrice)), nil
return (*hexutil.Big)(types.ToEthersUint(b.feeDecimals+9, gasPrice)), nil
beer-1 marked this conversation as resolved.
Show resolved Hide resolved
}

func (b *JSONRPCBackend) MaxPriorityFeePerGas() (*hexutil.Big, error) {
Expand Down
14 changes: 7 additions & 7 deletions jsonrpc/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func StartJSONRPC(
rpcServer := rpc.NewServer()
rpcServer.SetBatchLimits(jsonRPCConfig.BatchRequestLimit, jsonRPCConfig.BatchResponseMaxSize)

bkd, err := backend.NewJSONRPCBackend(app, logger, svrCtx, clientCtx, jsonRPCConfig)
bkd, err := backend.NewJSONRPCBackend(ctx, app, logger, svrCtx, clientCtx, jsonRPCConfig)
if err != nil {
return err
}
Expand All @@ -73,37 +73,37 @@ func StartJSONRPC(
{
Namespace: EthNamespace,
Version: apiVersion,
Service: ethns.NewEthAPI(logger, bkd),
Service: ethns.NewEthAPI(ctx, logger, bkd),
Public: true,
},
{
Namespace: EthNamespace,
Version: apiVersion,
Service: filters.NewFilterAPI(app, bkd, logger),
Service: filters.NewFilterAPI(ctx, app, bkd, logger),
Public: true,
},
{
Namespace: NetNamespace,
Version: apiVersion,
Service: netns.NewNetAPI(logger, bkd),
Service: netns.NewNetAPI(ctx, logger, bkd),
Public: true,
},
{
Namespace: Web3Namespace,
Version: apiVersion,
Service: web3ns.NewWeb3API(logger, bkd),
Service: web3ns.NewWeb3API(ctx, logger, bkd),
Public: true,
},
{
Namespace: TxPoolNamespace,
Version: apiVersion,
Service: txpoolns.NewTxPoolAPI(logger, bkd),
Service: txpoolns.NewTxPoolAPI(ctx, logger, bkd),
Public: true,
},
{
Namespace: CosmosNamespace,
Version: apiVersion,
Service: cosmosns.NewCosmosAPI(logger, bkd),
Service: cosmosns.NewCosmosAPI(ctx, logger, bkd),
Public: true,
},
}
Expand Down
4 changes: 2 additions & 2 deletions jsonrpc/namespaces/cosmos/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ type CosmosAPI struct {
}

// NewCosmosAPI creates an instance of the public ETH Web3 API.
func NewCosmosAPI(logger log.Logger, backend *backend.JSONRPCBackend) *CosmosAPI {
func NewCosmosAPI(ctx context.Context, logger log.Logger, backend *backend.JSONRPCBackend) *CosmosAPI {
api := &CosmosAPI{
ctx: context.TODO(),
ctx: ctx,
logger: logger.With("client", "json-rpc"),
backend: backend,
}
Expand Down
4 changes: 2 additions & 2 deletions jsonrpc/namespaces/eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ type EthAPI struct {
}

// NewEthAPI creates an instance of the public ETH Web3 API.
func NewEthAPI(logger log.Logger, backend *backend.JSONRPCBackend) *EthAPI {
func NewEthAPI(ctx context.Context, logger log.Logger, backend *backend.JSONRPCBackend) *EthAPI {
api := &EthAPI{
ctx: context.TODO(),
ctx: ctx,
logger: logger.With("client", "json-rpc"),
backend: backend,
}
Expand Down
40 changes: 25 additions & 15 deletions jsonrpc/namespaces/eth/filters/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type filter struct {

// FilterAPI is the eth_ filter namespace API
type FilterAPI struct {
ctx context.Context

app *app.MinitiaApp
backend *backend.JSONRPCBackend

Expand All @@ -63,9 +65,11 @@ type FilterAPI struct {
}

// NewFiltersAPI returns a new instance
func NewFilterAPI(app *app.MinitiaApp, backend *backend.JSONRPCBackend, logger log.Logger) *FilterAPI {
func NewFilterAPI(ctx context.Context, app *app.MinitiaApp, backend *backend.JSONRPCBackend, logger log.Logger) *FilterAPI {
logger = logger.With("api", "filter")
api := &FilterAPI{
ctx: ctx,

app: app,
backend: backend,

Expand Down Expand Up @@ -98,23 +102,27 @@ func (api *FilterAPI) clearUnusedFilters() {

var toUninstall []*subscription
for {
<-ticker.C
api.filtersMut.Lock()
for id, f := range api.filters {
if time.Since(f.lastUsed) > timeout {
toUninstall = append(toUninstall, f.s)
delete(api.filters, id)
select {
case <-ticker.C:
api.filtersMut.Lock()
for id, f := range api.filters {
if time.Since(f.lastUsed) > timeout {
toUninstall = append(toUninstall, f.s)
delete(api.filters, id)
}
}
}
api.filtersMut.Unlock()
api.filtersMut.Unlock()

// Unsubscribes are processed outside the lock to avoid the following scenario:
// event loop attempts broadcasting events to still active filters while
// Unsubscribe is waiting for it to process the uninstall request.
for _, s := range toUninstall {
api.uninstallSubscription(s)
// Unsubscribes are processed outside the lock to avoid the following scenario:
// event loop attempts broadcasting events to still active filters while
// Unsubscribe is waiting for it to process the uninstall request.
for _, s := range toUninstall {
api.uninstallSubscription(s)
}
toUninstall = nil
case <-api.ctx.Done():
return
}
toUninstall = nil
}
}

Expand Down Expand Up @@ -155,6 +163,8 @@ func (api *FilterAPI) eventLoop() {
case s := <-api.uninstall:
delete(api.subscriptions, s.id)
close(s.err)
case <-api.ctx.Done():
return
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions jsonrpc/namespaces/net/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ type NetAPI struct {
}

// NewNetAPI creates a new net API instance
func NewNetAPI(logger log.Logger, backend *backend.JSONRPCBackend) *NetAPI {
func NewNetAPI(ctx context.Context, logger log.Logger, backend *backend.JSONRPCBackend) *NetAPI {
return &NetAPI{
ctx: context.TODO(),
ctx: ctx,
logger: logger,
backend: backend,
}
Expand Down
4 changes: 2 additions & 2 deletions jsonrpc/namespaces/txpool/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ type TxPoolAPI struct {
}

// NewTxPoolAPI creates a new txpool API instance.
func NewTxPoolAPI(logger log.Logger, backend *backend.JSONRPCBackend) *TxPoolAPI {
func NewTxPoolAPI(ctx context.Context, logger log.Logger, backend *backend.JSONRPCBackend) *TxPoolAPI {
return &TxPoolAPI{
ctx: context.TODO(),
ctx: ctx,
logger: logger,
backend: backend,
}
Expand Down
4 changes: 2 additions & 2 deletions jsonrpc/namespaces/web3/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ type Web3API struct {
}

// NewWeb3API creates a new net API instance
func NewWeb3API(logger log.Logger, backend *backend.JSONRPCBackend) *Web3API {
func NewWeb3API(ctx context.Context, logger log.Logger, backend *backend.JSONRPCBackend) *Web3API {
return &Web3API{
ctx: context.TODO(),
ctx: ctx,
logger: logger,
backend: backend,
}
Expand Down
Loading