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 all 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
75 changes: 72 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,11 @@ 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
feeMutex sync.RWMutex

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

Expand All @@ -65,6 +71,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 +93,7 @@ func NewJSONRPCBackend(
return nil, err
}

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

Expand All @@ -113,7 +119,70 @@ 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) feeInfo() (string, uint8, error) {
b.feeMutex.RLock()
defer b.feeMutex.RUnlock()

if b.feeDenom == "" {
return "", 0, NewInternalError("jsonrpc is not ready")
}

return b.feeDenom, b.feeDecimals, 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.feeMutex.Lock()
b.feeDenom = feeDenom
b.feeDecimals = decimals
b.feeMutex.Unlock()

return nil
}

ticker := time.NewTicker(3 * 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
}
}
}

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

feeDenom, decimals, err := b.feeDenomWithDecimals()
feeDenom, feeDecimals, err := b.feeInfo()
if err != nil {
return nil, err
}
Expand All @@ -35,7 +35,7 @@ func (b *JSONRPCBackend) GetBalance(address common.Address, blockNrOrHash rpc.Bl
return nil, err
}

return (*hexutil.Big)(types.ToEthersUint(decimals, balance.BigInt())), nil
return (*hexutil.Big)(types.ToEthersUint(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
54 changes: 15 additions & 39 deletions jsonrpc/backend/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (b *JSONRPCBackend) EstimateGas(args rpctypes.TransactionArgs, blockNrOrHas
return hexutil.Uint64(0), err
}

_, decimals, err := b.feeDenomWithDecimals()
_, feeDecimals, err := b.feeInfo()
if err != nil {
return hexutil.Uint64(0), err
}
Expand All @@ -49,14 +49,14 @@ func (b *JSONRPCBackend) EstimateGas(args rpctypes.TransactionArgs, blockNrOrHas
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(feeDecimals, args.Value.ToInt())),
beer-1 marked this conversation as resolved.
Show resolved Hide resolved
})
} 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(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,26 @@ func (b *JSONRPCBackend) GasPrice() (*hexutil.Big, error) {
return nil, err
}

feeDenom, decimals, err := b.feeDenomWithDecimals()
feeDenom, feeDecimals, err := b.feeInfo()
if err != nil {
return nil, err
}

// Multiply by 1e9 to maintain precision during conversion
// This adds 9 decimal places to prevent truncation errors
const precisionMultiplier = 1e9

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

return (*hexutil.Big)(types.ToEthersUint(decimals+9, gasPrice)), nil
// Verify the result is within safe bounds
if gasPrice.BitLen() > 256 {
return nil, NewInternalError("gas price overflow")
}

return (*hexutil.Big)(types.ToEthersUint(feeDecimals+9, gasPrice)), nil
}

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
Loading
Loading