Skip to content

Commit

Permalink
add more json rpc support (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
beer-1 authored Jul 12, 2024
1 parent 60179f7 commit fb6e4c8
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 72 deletions.
19 changes: 9 additions & 10 deletions jsonrpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ The ETH JSON-RPC (Remote Procedure Call) is a protocol that allows clients to in

| namespace | api | supported | description |
| --------- | ------------------------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| web3 | web3_clientVersion | 🚫 | Returns the current client version. |
| web3 | web3_sha3 | 🚫 | Returns the Keccak-256 (not the standardized SHA3-256) of the given data. |
| web3 | web3_clientVersion | | Returns the current client version. |
| web3 | web3_sha3 | | Returns the Keccak-256 (not the standardized SHA3-256) of the given data. |
| net | net_version || Returns the current network ID. |
| net | net_peerCount | 🚫 | Returns the number of peers currently connected to the client. |
| net | net_listening | 🚫 | Returns true if the client is actively listening for network connections. |
| eth | eth_protocolVersion | 🚫 | Returns the current Ethereum protocol version. |
| eth | eth_syncing | 🚫 | Returns an object with data about the sync status or false if not syncing. |
| net | net_peerCount || Returns the number of peers currently connected to the client. |
| net | net_listening || Returns true if the client is actively listening for network connections. |
| eth | eth_syncing || Returns an object with data about the sync status or false if not syncing. |
| eth | eth_coinbase | 🚫 | Returns the client coinbase address. |
| eth | eth_chainId || Returns the chain id. |
| eth | eth_mining | 🚫 | Returns true if the client is actively mining new blocks. |
Expand Down Expand Up @@ -73,7 +72,7 @@ The ETH JSON-RPC (Remote Procedure Call) is a protocol that allows clients to in
| debug | debug_traceBlockByHash | 🚫 | Returns trace of a block by hash. |
| debug | debug_storageRangeAt | 🚫 | Returns a storage range at a specific position. |
| debug | debug_getBadBlocks | 🚫 | Returns list of bad blocks. |
| txpool | txpool_content | 🚫 | Returns all pending and queued transactions |
| txpool | txpool_inspect | 🚫 | Returns a textual summary of all pending and queued transactions |
| txpool | txpool_contentFrom | 🚫 | Retrieves the transactions contained within the txpool, returning pending and queued transactions of this address, grouped by nonce |
| txpool | txpool_status | 🚫 | Returns the number of transactions in pending and queued states |
| txpool | txpool_content | | Returns all pending and queued transactions |
| txpool | txpool_inspect | | Returns a textual summary of all pending and queued transactions |
| txpool | txpool_contentFrom | | Retrieves the transactions contained within the txpool, returning pending and queued transactions of this address, grouped by nonce |
| txpool | txpool_status | | Returns the number of transactions in pending and queued states |
34 changes: 34 additions & 0 deletions jsonrpc/backend/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,37 @@ func (b *JSONRPCBackend) ChainID() (*big.Int, error) {
sdkCtx := sdk.UnwrapSDKContext(queryCtx)
return types.ConvertCosmosChainIDToEthereumChainID(sdkCtx.ChainID()), nil
}

func (b *JSONRPCBackend) Syncing() (interface{}, error) {
status, err := b.clientCtx.Client.Status(b.ctx)
if err != nil {
return nil, err
}

if !status.SyncInfo.CatchingUp {
return false, nil
}

latestHeight := status.SyncInfo.LatestBlockHeight

// Otherwise gather the block sync stats
return map[string]interface{}{
"startingBlock": hexutil.Uint64(status.SyncInfo.EarliestBlockHeight),
"currentBlock": hexutil.Uint64(latestHeight),
"highestBlock": hexutil.Uint64(latestHeight),
"syncedAccounts": hexutil.Uint64(latestHeight),
"syncedAccountBytes": hexutil.Uint64(latestHeight),
"syncedBytecodes": hexutil.Uint64(latestHeight),
"syncedBytecodeBytes": hexutil.Uint64(latestHeight),
"syncedStorage": hexutil.Uint64(latestHeight),
"syncedStorageBytes": hexutil.Uint64(latestHeight),
"healedTrienodes": hexutil.Uint64(latestHeight),
"healedTrienodeBytes": hexutil.Uint64(latestHeight),
"healedBytecodes": hexutil.Uint64(latestHeight),
"healedBytecodeBytes": hexutil.Uint64(latestHeight),
"healingTrienodes": hexutil.Uint64(latestHeight),
"healingBytecode": hexutil.Uint64(latestHeight),
"txIndexFinishedBlocks": hexutil.Uint64(latestHeight),
"txIndexRemainingBlocks": hexutil.Uint64(latestHeight),
}, nil
}
21 changes: 21 additions & 0 deletions jsonrpc/backend/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import (
"strconv"
"strings"

rpcclient "github.com/cometbft/cometbft/rpc/client"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/ethereum/go-ethereum/common/hexutil"
)

func (b *JSONRPCBackend) Version() (string, error) {
Expand All @@ -23,3 +26,21 @@ func (b *JSONRPCBackend) Version() (string, error) {

return version, err
}

func (b *JSONRPCBackend) PeerCount() (hexutil.Uint, error) {
netInfo, err := b.clientCtx.Client.(rpcclient.NetworkClient).NetInfo(b.ctx)
if err != nil {
return hexutil.Uint(0), err
}

return hexutil.Uint(netInfo.NPeers), nil
}

func (b *JSONRPCBackend) Listening() (bool, error) {
netInfo, err := b.clientCtx.Client.(rpcclient.NetworkClient).NetInfo(b.ctx)
if err != nil {
return false, err
}

return netInfo.Listening, nil
}
119 changes: 119 additions & 0 deletions jsonrpc/backend/txpool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package backend

import (
"fmt"

rpcclient "github.com/cometbft/cometbft/rpc/client"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"

rpctypes "github.com/initia-labs/minievm/jsonrpc/types"
"github.com/initia-labs/minievm/x/evm/keeper"
)

func (b *JSONRPCBackend) TxPoolContent() (map[string]map[string]map[string]*rpctypes.RPCTransaction, error) {
content := map[string]map[string]map[string]*rpctypes.RPCTransaction{
"pending": make(map[string]map[string]*rpctypes.RPCTransaction),
"queued": make(map[string]map[string]*rpctypes.RPCTransaction),
}

limit := int(100)
pending, err := b.clientCtx.Client.(rpcclient.MempoolClient).UnconfirmedTxs(b.ctx, &limit)
if err != nil {
return nil, err
}

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

chainID, err := b.ChainID()
if err != nil {
return nil, err
}

txUtils := keeper.NewTxUtils(b.app.EVMKeeper)
for _, tx := range pending.Txs {
cosmosTx, err := b.app.TxDecode(tx)
if err != nil {
return nil, err
}

ethTx, account, err := txUtils.ConvertCosmosTxToEthereumTx(ctx, cosmosTx)
if err != nil {
return nil, err
}
if ethTx == nil {
continue
}

dump, ok := content["pending"][account.Hex()]
if !ok {
dump = make(map[string]*rpctypes.RPCTransaction)
content["pending"][account.Hex()] = dump
}

dump[fmt.Sprintf("%d", ethTx.Nonce())] = rpctypes.NewRPCTransaction(ethTx, common.Hash{}, 0, 0, chainID)
}

return content, nil
}

func (b *JSONRPCBackend) TxPoolContentFrom(addr common.Address) (map[string]map[string]*rpctypes.RPCTransaction, error) {
content, err := b.TxPoolContent()
if err != nil {
return nil, err
}

dump := content["pending"][addr.Hex()]
accountContent := make(map[string]map[string]*rpctypes.RPCTransaction, 2)
accountContent["pending"] = dump
accountContent["queued"] = make(map[string]*rpctypes.RPCTransaction)

return accountContent, nil
}

// Status returns the number of pending and queued transaction in the pool.
func (b *JSONRPCBackend) TxPoolStatus() (map[string]hexutil.Uint, error) {
numUnconfirmedTxs, err := b.clientCtx.Client.(rpcclient.MempoolClient).NumUnconfirmedTxs(b.ctx)
if err != nil {
return nil, err
}

return map[string]hexutil.Uint{
"pending": hexutil.Uint(numUnconfirmedTxs.Count),
"queued": hexutil.Uint(0),
}, nil
}

// Inspect retrieves the content of the transaction pool and flattens it into an
// easily inspectable list.
func (b *JSONRPCBackend) TxPoolInspect() (map[string]map[string]map[string]string, error) {
inspectContent := map[string]map[string]map[string]string{
"pending": make(map[string]map[string]string),
"queued": make(map[string]map[string]string),
}

content, err := b.TxPoolContent()
if err != nil {
return nil, err
}

// Define a formatter to flatten a transaction into a string
var format = func(tx *rpctypes.RPCTransaction) string {
if to := tx.To; to != nil {
return fmt.Sprintf("%s: %v wei + %v gas × %v wei", tx.To.Hex(), tx.Value, tx.Gas, tx.GasPrice)
}
return fmt.Sprintf("contract creation: %v wei + %v gas × %v wei", tx.Value, tx.Gas, tx.GasPrice)
}
// Flatten the pending transactions
for account, txs := range content["pending"] {
dump := make(map[string]string)
for _, tx := range txs {
dump[fmt.Sprintf("%d", tx.Nonce)] = format(tx)
}
inspectContent["pending"][account] = dump
}
return inspectContent, nil
}
11 changes: 11 additions & 0 deletions jsonrpc/backend/web3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package backend

// ClientVersion returns the node name
func (b *JSONRPCBackend) ClientVersion() (string, error) {
status, err := b.clientCtx.Client.Status(b.ctx)
if err != nil {
return "", err
}

return status.NodeInfo.Version, nil
}
31 changes: 18 additions & 13 deletions jsonrpc/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ import (
"net/http"

"github.com/gorilla/mux"
ethns "github.com/initia-labs/minievm/jsonrpc/namespaces/eth"
"github.com/initia-labs/minievm/jsonrpc/namespaces/eth/filters"

// "github.com/initia-labs/minievm/jsonrpc/namespaces/eth/filters"
netns "github.com/initia-labs/minievm/jsonrpc/namespaces/net"
"github.com/rs/cors"
"golang.org/x/net/netutil"
"golang.org/x/sync/errgroup"
Expand All @@ -24,6 +19,11 @@ import (
"github.com/initia-labs/minievm/app"
"github.com/initia-labs/minievm/jsonrpc/backend"
"github.com/initia-labs/minievm/jsonrpc/config"
ethns "github.com/initia-labs/minievm/jsonrpc/namespaces/eth"
"github.com/initia-labs/minievm/jsonrpc/namespaces/eth/filters"
netns "github.com/initia-labs/minievm/jsonrpc/namespaces/net"
txpoolns "github.com/initia-labs/minievm/jsonrpc/namespaces/txpool"
web3ns "github.com/initia-labs/minievm/jsonrpc/namespaces/web3"
)

// RPC namespaces and API version
Expand All @@ -32,8 +32,8 @@ const (
EthNamespace = "eth"
NetNamespace = "net"
TxPoolNamespace = "txpool"
Web3Namespace = "web3"
// TODO: support more namespaces
Web3Namespace = "web3"
PersonalNamespace = "personal"
DebugNamespace = "debug"
MinerNamespace = "miner"
Expand Down Expand Up @@ -73,13 +73,18 @@ func StartJSONRPC(
Service: netns.NewNetAPI(svrCtx.Logger, bkd),
Public: true,
},
// TODO: implement more namespaces
//{
// Namespace: TxPoolNamespace,
// Version: apiVersion,
// Service: txpool.NewTxPoolAPI(svrCtx.Logger, bkd),
// Public: true,
//},
{
Namespace: Web3Namespace,
Version: apiVersion,
Service: web3ns.NewWeb3API(svrCtx.Logger, bkd),
Public: true,
},
{
Namespace: TxPoolNamespace,
Version: apiVersion,
Service: txpoolns.NewTxPoolAPI(svrCtx.Logger, bkd),
Public: true,
},
}

for _, api := range apis {
Expand Down
31 changes: 15 additions & 16 deletions jsonrpc/namespaces/eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type EthEthereumAPI interface {
// Allows developers to both send ETH from one address to another, write data
// on-chain, and interact with smart contracts.
SendRawTransaction(data hexutil.Bytes) (common.Hash, error)
//SendTransaction(args rpctypes.TransactionArgs) (common.Hash, error)
// SendTransaction(args rpctypes.TransactionArgs) (common.Hash, error)
// eth_sendPrivateTransaction
// eth_cancel PrivateTransaction

Expand All @@ -67,7 +67,7 @@ type EthEthereumAPI interface {
// smart contracts. However, no data is published to the Ethereum network.
Call(args rpctypes.TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, so *rpctypes.StateOverride, bo *rpctypes.BlockOverrides) (hexutil.Bytes, error)

// // Chain Information
// Chain Information
// //
// // Returns information on the Ethereum network and internal settings.
// ProtocolVersion() hexutil.Uint
Expand All @@ -77,8 +77,8 @@ type EthEthereumAPI interface {
MaxPriorityFeePerGas() (*hexutil.Big, error)
ChainId() *hexutil.Big

// // Other
// Syncing() (interface{}, error)
// Other
Syncing() (interface{}, error)
// Coinbase() (string, error)
// Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error)
// GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error)
Expand Down Expand Up @@ -384,18 +384,17 @@ func (api *EthAPI) Mining() bool {
// * others *
// *************************************

// TODO: Implement eth_syncing
//// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not
//// yet received the latest block headers from its pears. In case it is synchronizing:
//// - startingBlock: block number this node started to synchronize from
//// - currentBlock: block number this node is currently importing
//// - highestBlock: block number of the highest block header this node has received from peers
//// - pulledStates: number of state entries processed until now
//// - knownStates: number of known state entries that still need to be pulled
//func (e *EthAPI) Syncing() (interface{}, error) {
// e.logger.Debug("eth_syncing")
// return e.backend.Syncing()
//}
// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not
// yet received the latest block headers from its pears. In case it is synchronizing:
// - startingBlock: block number this node started to synchronize from
// - currentBlock: block number this node is currently importing
// - highestBlock: block number of the highest block header this node has received from peers
// - pulledStates: number of state entries processed until now
// - knownStates: number of known state entries that still need to be pulled
func (e *EthAPI) Syncing() (interface{}, error) {
e.logger.Debug("eth_syncing")
return e.backend.Syncing()
}

// TODO: Implement eth_coinbase
//// Coinbase is the address that staking rewards will be send to (alias for Etherbase).
Expand Down
16 changes: 7 additions & 9 deletions jsonrpc/namespaces/net/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,13 @@ func NewNetAPI(logger log.Logger, backend *backend.JSONRPCBackend) *NetAPI {
}
}

// TODO: implement net_listening
//func (api *NetAPI) Listening() bool {
// return true
//}

// TODO: implement net_peerCount
//func (api *NetAPI) PeerCount() hexutil.Uint {
// return hexutil.Uint(0)
//}
func (api *NetAPI) Listening() (bool, error) {
return api.backend.Listening()
}

func (api *NetAPI) PeerCount() (hexutil.Uint, error) {
return api.backend.PeerCount()
}

func (api *NetAPI) Version() string {
v, err := api.backend.Version()
Expand Down
Loading

0 comments on commit fb6e4c8

Please sign in to comment.