Skip to content

Commit

Permalink
feat(rpc): chain RPCs
Browse files Browse the repository at this point in the history
All APIs listed in the issue are implemented under chain rpc namespace.

These APIs are not implemented:

    /num_unconfirmed_txs, chain.unconfirmed_txs returned the total txs
    /tx_search, it's more useful for tx subscription, we don't have the needs yet
    /block_search, it's more useful for block subscription
    /check_tx
    /commit, I don't think we have this kind of info
  • Loading branch information
Yaiba authored Dec 12, 2024
1 parent 5abc566 commit f9c922f
Show file tree
Hide file tree
Showing 18 changed files with 760 additions and 16 deletions.
7 changes: 6 additions & 1 deletion app/node/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (

rpcserver "github.com/kwilteam/kwil-db/node/services/jsonrpc"
"github.com/kwilteam/kwil-db/node/services/jsonrpc/adminsvc"
"github.com/kwilteam/kwil-db/node/services/jsonrpc/chainsvc"
"github.com/kwilteam/kwil-db/node/services/jsonrpc/funcsvc"
"github.com/kwilteam/kwil-db/node/services/jsonrpc/usersvc"
)
Expand Down Expand Up @@ -94,7 +95,7 @@ func buildServer(ctx context.Context, d *coreDependencies) *server {
usersvc.WithPrivateMode(d.cfg.RPC.Private),
usersvc.WithChallengeExpiry(d.cfg.RPC.ChallengeExpiry),
usersvc.WithChallengeRateLimit(d.cfg.RPC.ChallengeRateLimit),
// usersvc.WithBlockAgeHealth(6*totalConsensusTimeouts.Dur()),
// usersvc.WithBlockAgeHealth(6*totalConsensusTimeouts.Dur()),
)

rpcServerLogger := d.logger.New("RPC")
Expand Down Expand Up @@ -125,6 +126,10 @@ func buildServer(ctx context.Context, d *coreDependencies) *server {
jsonRPCAdminServer.RegisterSvc(&funcsvc.Service{})
}

chainRpcSvcLogger := d.logger.New("CHAIN")
jsonChainSvc := chainsvc.NewService(chainRpcSvcLogger, node, vs, d.genesisCfg)
jsonRPCServer.RegisterSvc(jsonChainSvc)

s := &server{
cfg: d.cfg,
closers: closers,
Expand Down
7 changes: 3 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import (
"time"

"github.com/kwilteam/kwil-db/core/log"
ktypes "github.com/kwilteam/kwil-db/core/types"
"github.com/kwilteam/kwil-db/node/types"
"github.com/kwilteam/kwil-db/core/types"

"github.com/pelletier/go-toml/v2"
)
Expand Down Expand Up @@ -41,7 +40,7 @@ type GenesisConfig struct {
// Leader is the leader's public key.
Leader types.HexBytes `json:"leader"`
// Validators is the list of genesis validators (including the leader).
Validators []*ktypes.Validator `json:"validators"`
Validators []*types.Validator `json:"validators"`

// MaxBlockSize is the maximum size of a block in bytes.
MaxBlockSize int64 `json:"max_block_size"`
Expand Down Expand Up @@ -89,7 +88,7 @@ func DefaultGenesisConfig() *GenesisConfig {
ChainID: "kwil-test-chain",
InitialHeight: 0,
Leader: types.HexBytes{},
Validators: []*ktypes.Validator{},
Validators: []*types.Validator{},
DisabledGasCosts: true,
JoinExpiry: 14400,
VoteExpiry: 108000,
Expand Down
3 changes: 2 additions & 1 deletion contrib/docker/compose/postgres/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ services:
# POSTGRES_USER: kwild
# POSTGRES_PASSWORD: kwild
# POSTGRES_DB: kwild
volumes:
volumes:
- kwildb:/var/lib/postgresql/data

volumes:
kwildb:
driver: local

21 changes: 21 additions & 0 deletions core/rpc/client/chain/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package chain

import (
"context"

"github.com/kwilteam/kwil-db/core/types"
chaintypes "github.com/kwilteam/kwil-db/core/types/chain"
)

type Client interface {
Version(ctx context.Context) (string, error)
BlockByHeight(ctx context.Context, height int64) (*chaintypes.Block, error)
BlockByHash(ctx context.Context, hash types.Hash) (*chaintypes.Block, error)
BlockResultByHeight(ctx context.Context, height int64) (*chaintypes.BlockResult, error)
BlockResultByHash(ctx context.Context, hash types.Hash) (*chaintypes.BlockResult, error)
Tx(ctx context.Context, hash types.Hash) (*chaintypes.Tx, error)
Genesis(ctx context.Context) (*chaintypes.Genesis, error)
ConsensusParams(ctx context.Context) (*types.ConsensusParams, error)
Validators(ctx context.Context) (height int64, validators []*types.Validator, err error)
UnconfirmedTxs(ctx context.Context) (total int, txs []chaintypes.NamedTx, err error)
}
145 changes: 145 additions & 0 deletions core/rpc/client/chain/jsonrpc/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package jsonrpc

import (
"context"
"net/url"

rpcclient "github.com/kwilteam/kwil-db/core/rpc/client"
"github.com/kwilteam/kwil-db/core/rpc/client/chain"
"github.com/kwilteam/kwil-db/core/rpc/client/user"
userClient "github.com/kwilteam/kwil-db/core/rpc/client/user/jsonrpc"
chainjson "github.com/kwilteam/kwil-db/core/rpc/json/chain"
userjson "github.com/kwilteam/kwil-db/core/rpc/json/user"
"github.com/kwilteam/kwil-db/core/types"
chaintypes "github.com/kwilteam/kwil-db/core/types/chain"
)

// Client is a chain RPC client. It provides all methods of the user RPC
// service, plus methods that are specific to the chain service.
type Client struct {
*userClient.Client // expose all user service methods, and methods for chain svc
}

// Version reports the version of the running node.
func (c *Client) Version(ctx context.Context) (string, error) {
req := &userjson.VersionRequest{}
res := &userjson.VersionResponse{}
err := c.CallMethod(ctx, string(chainjson.MethodVersion), req, res)
if err != nil {
return "", err
}
return res.KwilVersion, err
}

func (c *Client) BlockByHeight(ctx context.Context, height int64) (*chaintypes.Block, error) {
req := &chainjson.BlockRequest{
Height: height,
}
res := &chainjson.BlockResponse{}
err := c.CallMethod(ctx, string(chainjson.MethodBlock), req, res)
if err != nil {
return nil, err
}
return (*chaintypes.Block)(res), nil
}

func (c *Client) BlockByHash(ctx context.Context, hash types.Hash) (*chaintypes.Block, error) {
req := &chainjson.BlockRequest{
Hash: hash,
}
res := &chainjson.BlockResponse{}
err := c.CallMethod(ctx, string(chainjson.MethodBlock), req, res)
if err != nil {
return nil, err
}
return (*chaintypes.Block)(res), nil
}

func (c *Client) BlockResultByHeight(ctx context.Context, height int64) (*chaintypes.BlockResult, error) {
req := &chainjson.BlockResultRequest{
Height: height,
}
res := &chainjson.BlockResultResponse{}
err := c.CallMethod(ctx, string(chainjson.MethodBlockResult), req, res)
if err != nil {
return nil, err
}
return (*chaintypes.BlockResult)(res), nil
}

func (c *Client) BlockResultByHash(ctx context.Context, hash types.Hash) (*chaintypes.BlockResult, error) {
req := &chainjson.BlockResultRequest{
Hash: hash,
}
res := &chainjson.BlockResultResponse{}
err := c.CallMethod(ctx, string(chainjson.MethodBlockResult), req, res)
if err != nil {
return nil, err
}
return (*chaintypes.BlockResult)(res), nil
}

func (c *Client) Tx(ctx context.Context, hash types.Hash) (*chaintypes.Tx, error) {
req := &chainjson.TxRequest{
Hash: hash,
}
res := &chainjson.TxResponse{}
err := c.CallMethod(ctx, string(chainjson.MethodTx), req, res)
if err != nil {
return nil, err
}
return (*chaintypes.Tx)(res), err
}

func (c *Client) Genesis(ctx context.Context) (*chaintypes.Genesis, error) {
req := &chainjson.GenesisRequest{}
res := &chainjson.GenesisResponse{}
err := c.CallMethod(ctx, string(chainjson.MethodGenesis), req, res)
if err != nil {
return nil, err
}
return (*chaintypes.Genesis)(res), err
}

func (c *Client) ConsensusParams(ctx context.Context) (*types.ConsensusParams, error) {
req := &chainjson.ConsensusParamsRequest{}
res := &chainjson.ConsensusParamsResponse{}
err := c.CallMethod(ctx, string(chainjson.MethodConsensusParams), req, res)
if err != nil {
return nil, err
}
return (*types.ConsensusParams)(res), nil
}

func (c *Client) Validators(ctx context.Context) (int64, []*types.Validator, error) {
req := &chainjson.ValidatorsRequest{}
res := &chainjson.ValidatorsResponse{}
err := c.CallMethod(ctx, string(chainjson.MethodValidators), req, res)
if err != nil {
return 0, nil, err
}

return res.Height, res.Validators, nil
}

func (c *Client) UnconfirmedTxs(ctx context.Context) (total int, tx []chaintypes.NamedTx, err error) {
req := &chainjson.UnconfirmedTxsRequest{}
res := &chainjson.UnconfirmedTxsResponse{}
err = c.CallMethod(ctx, string(chainjson.MethodUnconfirmedTxs), req, res)
if err != nil {
return 0, nil, err
}

return res.Total, res.Txs, nil
}

// NewClient constructs a new chain Client.
func NewClient(u *url.URL, opts ...rpcclient.RPCClientOpts) *Client {
userClient := userClient.NewClient(u, opts...)
return &Client{
Client: userClient,
}
}

var _ user.TxSvcClient = (*Client)(nil) // via embedded userClient.Client
var _ chain.Client = (*Client)(nil) // with extra methods
32 changes: 32 additions & 0 deletions core/rpc/json/chain/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package chain

import (
"github.com/kwilteam/kwil-db/core/types"
)

type HealthRequest struct{}

type BlockRequest struct {
Height int64 `json:"height"`
// Hash is the block hash. If both Height and Hash are provided, hash will be used
Hash types.Hash `json:"hash"`
}

type BlockResultRequest struct {
Height int64 `json:"height"`
// Hash is the block hash. If both Height and Hash are provided, hash will be used
Hash types.Hash `json:"hash"`
}

type TxRequest struct {
Hash types.Hash `json:"hash"`
}

type GenesisRequest struct{}
type ConsensusParamsRequest struct{}
type ValidatorsRequest struct {
Height int64 `json:"height"`
}
type UnconfirmedTxsRequest struct {
Limit int `json:"limit"`
}
17 changes: 17 additions & 0 deletions core/rpc/json/chain/methods.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package chain

import (
jsonrpc "github.com/kwilteam/kwil-db/core/rpc/json"
)

const (
MethodVersion jsonrpc.Method = "chain.version"
MethodHealth jsonrpc.Method = "chain.health"
MethodBlock jsonrpc.Method = "chain.block"
MethodBlockResult jsonrpc.Method = "chain.block_result"
MethodTx jsonrpc.Method = "chain.tx"
MethodGenesis jsonrpc.Method = "chain.genesis"
MethodConsensusParams jsonrpc.Method = "chain.consensus_params"
MethodValidators jsonrpc.Method = "chain.validators"
MethodUnconfirmedTxs jsonrpc.Method = "chain.unconfirmed_txs"
)
66 changes: 66 additions & 0 deletions core/rpc/json/chain/responses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package chain

import (
"encoding/json"

"github.com/kwilteam/kwil-db/core/types"
chaintypes "github.com/kwilteam/kwil-db/core/types/chain"
)

// HealthResponse is the health check response.
type HealthResponse struct {
ChainID string `json:"chain_id"`
Height int64 `json:"height"`
Healthy bool `json:"healthy"`
}

// BlockResponse is the block information
type BlockResponse chaintypes.Block

type BlockResultResponse chaintypes.BlockResult

type TxResponse chaintypes.Tx

// GenesisResponse is the same as kwil-db/config.GenesisConfig, with JSON tags.
type GenesisResponse chaintypes.Genesis

type ConsensusParamsResponse types.ConsensusParams

func (r ConsensusParamsResponse) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
// MaxBlockSize is the maximum size of a block in bytes.
MaxBlockSize int64 `json:"max_block_size"`
// JoinExpiry is the number of blocks after which the validators
// join request expires if not approved.
JoinExpiry int64 `json:"join_expiry"`
// VoteExpiry is the default number of blocks after which the validators
// vote expires if not approved.
VoteExpiry int64 `json:"vote_expiry"`
// DisabledGasCosts dictates whether gas costs are disabled.
DisabledGasCosts bool `json:"disabled_gas_costs"`

// MigrationStatus determines the status of the migration.
MigrationStatus string `json:"migration_status"`

// MaxVotesPerTx is the maximum number of votes that can be included in a
// single transaction.
MaxVotesPerTx int64 `json:"max_votes_per_tx"`
}{
MaxBlockSize: r.MaxBlockSize,
JoinExpiry: r.JoinExpiry,
VoteExpiry: r.VoteExpiry,
DisabledGasCosts: r.DisabledGasCosts,
MigrationStatus: string(r.MigrationStatus),
MaxVotesPerTx: r.MaxVotesPerTx,
})
}

type ValidatorsResponse struct {
Height int64 `json:"height"`
Validators []*types.Validator `json:"validators"`
}

type UnconfirmedTxsResponse struct {
Total int `json:"total"`
Txs []chaintypes.NamedTx `json:"txs"`
}
Loading

0 comments on commit f9c922f

Please sign in to comment.