Skip to content

Commit

Permalink
feat(rpc): chain RPCs
Browse files Browse the repository at this point in the history
  • Loading branch information
Yaiba committed Dec 11, 2024
1 parent 58b3dc4 commit e12bf7c
Show file tree
Hide file tree
Showing 12 changed files with 528 additions and 14 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, 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

31 changes: 31 additions & 0 deletions core/rpc/json/chain/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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 ValidatorsRequest struct {
Height int64 `json:"height"`
}
type UnconfirmedTxsRequest struct {
Limit int `json:"limit"`
}
16 changes: 16 additions & 0 deletions core/rpc/json/chain/methods.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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"
MethodValidators jsonrpc.Method = "chain.validators"
MethodUnconfirmedTxs jsonrpc.Method = "chain.unconfirmed_txs"
)
87 changes: 87 additions & 0 deletions core/rpc/json/chain/responses.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package chain

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

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

type BlockHeader types.BlockHeader

func (b BlockHeader) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Version uint16 `json:"version"`
Height int64 `json:"height"`
NumTxns uint32 `json:"num_txns"`
PrevHash types.Hash `json:"prev_hash"`
PrevAppHash types.Hash `json:"prev_app_hash"`
// Timestamp is the unix millisecond timestamp
Timestamp int64 `json:"timestamp"`
MerkleRoot types.Hash `json:"merkle_root"`
ValidatorSetHash types.Hash `json:"validator_set_hash"`
}{
Version: b.Version,
Height: b.Height,
NumTxns: b.NumTxns,
PrevHash: b.PrevHash,
PrevAppHash: b.PrevAppHash,
Timestamp: b.Timestamp.UnixMilli(),
MerkleRoot: b.MerkleRoot,
ValidatorSetHash: b.ValidatorSetHash,
})
}

// BlockResponse is the block information
type BlockResponse struct {
Header *BlockHeader `json:"header"`
Txns [][]byte `json:"txns"`
Signature []byte `json:"signature"`
Hash types.Hash `json:"hash"`
AppHash types.Hash `json:"app_hash"`
}

type BlockResultResponse struct {
Height int64 `json:"height"`
TxResults []types.TxResult `json:"tx_results"`
}

type GenesisResponse struct {
ChainID string `json:"chain_id"`
// Leader is the leader's public key.
Leader types.HexBytes `json:"leader"`
// Validators is the list of genesis validators (including the leader).
Validators []*types.Validator `json:"validators"`
// 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"`
// MaxVotesPerTx is the maximum number of votes that can be included in a
// single transaction.
MaxVotesPerTx int64 `json:"max_votes_per_tx"`
}
type ValidatorsResponse struct {
Height int64 `json:"height"`
Validators []*types.Validator `json:"validators"`
}

type NamedTx struct {
Hash types.Hash `json:"hash"`
Tx []byte `json:"tx"`
}

type UnconfirmedTxsResponse struct {
Total int `json:"total"`
Txs []NamedTx `json:"txs"`
}
12 changes: 12 additions & 0 deletions core/types/chain/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Package types contains the type used by the chain stat RPC client and servers.
package types

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

type ChainTx struct {
Hash types.Hash `json:"hash"`
Height int64 `json:"height"`
Index uint32 `json:"index"`
Tx []byte `json:"tx"`
TxResult *types.TxResult `json:"tx_result"`
}
19 changes: 19 additions & 0 deletions node/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,22 @@ func (n *Node) getBlkHeight(ctx context.Context, height int64) (types.Hash, type
}
return types.Hash{}, types.Hash{}, nil, ErrBlkNotFound
}

// BlockByHeight returns the block by height. If height <= 0, the latest block
// will be returned.
func (n *Node) BlockByHeight(_ context.Context, height int64) (types.Hash, *ktypes.Block, types.Hash, error) {
if height <= 0 { // I think this is correct since block height starts from 1
height, _, _ = n.bki.Best()
}
return n.bki.GetByHeight(height)
}

// BlockByHash returns the block by block hash.
func (n *Node) BlockByHash(_ context.Context, hash types.Hash) (*ktypes.Block, types.Hash, error) {
return n.bki.Get(hash)
}

// BlockResultByHash returns the block result by block hash.
func (n *Node) BlockResultByHash(_ context.Context, hash types.Hash) ([]ktypes.TxResult, error) {
return n.bki.Results(hash)
}
38 changes: 38 additions & 0 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/kwilteam/kwil-db/core/log"
ktypes "github.com/kwilteam/kwil-db/core/types"
adminTypes "github.com/kwilteam/kwil-db/core/types/admin"
chainTypes "github.com/kwilteam/kwil-db/core/types/chain"
"github.com/kwilteam/kwil-db/node/peers"
"github.com/kwilteam/kwil-db/node/types"

Expand Down Expand Up @@ -515,6 +516,43 @@ func (n *Node) BroadcastTx(ctx context.Context, tx *ktypes.Transaction, _ /*sync
}, nil
}

// ChainTx return tx info that is used in Chain rpc.
func (n *Node) ChainTx(_ context.Context, hash types.Hash) (*chainTypes.ChainTx, error) {
raw, height, blkHash, blkIdx, err := n.bki.GetTx(hash)
if err != nil {
return nil, err
}
blkResults, err := n.bki.Results(blkHash)
if err != nil {
return nil, err
}
if int(blkIdx) >= len(blkResults) {
return nil, errors.New("invalid block index")
}
res := blkResults[blkIdx]
return &chainTypes.ChainTx{
Hash: hash,
Height: height,
Index: blkIdx,
Tx: raw,
TxResult: &res,
}, nil
}

// ChainUnconfirmedTx return unconfirmed tx info that is used in Chain rpc.
func (n *Node) ChainUnconfirmedTx(_ context.Context, limit int) (int, []types.NamedTx) {
total := n.mp.Size()
if limit <= 0 {
return total, nil
}
return n.mp.Size(), n.mp.PeekN(limit)
}

func (n *Node) BlockHeight(ctx context.Context) int64 {
height, _, _ := n.bki.Best()
return height
}

var RequiredStreamProtocols = []protocol.ID{
ProtocolIDDiscover,
ProtocolIDTx,
Expand Down
Loading

0 comments on commit e12bf7c

Please sign in to comment.