Skip to content

Commit

Permalink
support chain.validators and chain.consensus_params
Browse files Browse the repository at this point in the history
  • Loading branch information
Yaiba committed Dec 11, 2024
1 parent 8312d86 commit 17b1807
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 56 deletions.
2 changes: 1 addition & 1 deletion app/node/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func buildServer(ctx context.Context, d *coreDependencies) *server {
}

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

s := &server{
Expand Down
1 change: 1 addition & 0 deletions core/rpc/json/chain/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type TxRequest struct {
}

type GenesisRequest struct{}
type ConsensusParamsRequest struct{}
type ValidatorsRequest struct {
Height int64 `json:"height"`
}
Expand Down
17 changes: 9 additions & 8 deletions core/rpc/json/chain/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import (
)

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"
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"
)
34 changes: 34 additions & 0 deletions core/rpc/json/chain/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package chain

import (
"encoding/json"

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

Expand Down Expand Up @@ -51,6 +52,7 @@ type BlockResultResponse struct {
TxResults []types.TxResult `json:"tx_results"`
}

// GenesisResponse is the same as kwil-db/config.GenesisConfig, with JSON tags.
type GenesisResponse struct {
ChainID string `json:"chain_id"`
// Leader is the leader's public key.
Expand All @@ -71,6 +73,38 @@ type GenesisResponse struct {
// single transaction.
MaxVotesPerTx int64 `json:"max_votes_per_tx"`
}

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"`
Expand Down
6 changes: 3 additions & 3 deletions node/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,19 +302,19 @@ func (n *Node) getBlkHeight(ctx context.Context, height int64) (types.Hash, type

// 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) {
func (n *Node) BlockByHeight(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) {
func (n *Node) BlockByHash(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) {
func (n *Node) BlockResultByHash(hash types.Hash) ([]ktypes.TxResult, error) {
return n.bki.Results(hash)
}
4 changes: 4 additions & 0 deletions node/consensus/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ func (ce *ConsensusEngine) CheckTx(ctx context.Context, tx []byte) error {
return ce.blockProcessor.CheckTx(ctx, tx, false)
}

func (ce *ConsensusEngine) ConsensusParams() *ktypes.ConsensusParams {
return ce.blockProcessor.ConsensusParams()
}

func (ce *ConsensusEngine) executeBlock(ctx context.Context, blkProp *blockProposal) error {
defer func() {
ce.stateInfo.mtx.Lock()
Expand Down
1 change: 1 addition & 0 deletions node/consensus/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@ type BlockProcessor interface {
CheckTx(ctx context.Context, tx []byte, recheck bool) error

GetValidators() []*ktypes.Validator
ConsensusParams() *ktypes.ConsensusParams
}
2 changes: 2 additions & 0 deletions node/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type ConsensusEngine interface {
blkRequester consensus.BlkRequester, stateResetter consensus.ResetStateBroadcaster, discoveryBroadcaster consensus.DiscoveryReqBroadcaster) error

CheckTx(ctx context.Context, tx []byte) error

ConsensusParams() *ktypes.ConsensusParams
}

type SnapshotStore interface {
Expand Down
10 changes: 7 additions & 3 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ func (n *Node) BroadcastTx(ctx context.Context, tx *ktypes.Transaction, _ /*sync
}

// ChainTx return tx info that is used in Chain rpc.
func (n *Node) ChainTx(_ context.Context, hash types.Hash) (*chainTypes.ChainTx, error) {
func (n *Node) ChainTx(hash types.Hash) (*chainTypes.ChainTx, error) {
raw, height, blkHash, blkIdx, err := n.bki.GetTx(hash)
if err != nil {
return nil, err
Expand All @@ -540,19 +540,23 @@ func (n *Node) ChainTx(_ context.Context, hash types.Hash) (*chainTypes.ChainTx,
}

// ChainUnconfirmedTx return unconfirmed tx info that is used in Chain rpc.
func (n *Node) ChainUnconfirmedTx(_ context.Context, limit int) (int, []types.NamedTx) {
func (n *Node) ChainUnconfirmedTx(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 {
func (n *Node) BlockHeight() int64 {
height, _, _ := n.bki.Best()
return height
}

func (n *Node) ConsensusParams() *ktypes.ConsensusParams {
return n.ce.ConsensusParams()
}

var RequiredStreamProtocols = []protocol.ID{
ProtocolIDDiscover,
ProtocolIDTx,
Expand Down
89 changes: 48 additions & 41 deletions node/services/jsonrpc/chainsvc/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ var (
// Node specifies the methods required for chain service to interact with the blockchain.
type Node interface {
// BlockByHeight returns block info at height. If height=0, the latest block will be returned.
BlockByHeight(ctx context.Context, height int64) (ktypes.Hash, *ktypes.Block, ktypes.Hash, error)
BlockByHash(ctx context.Context, hash ktypes.Hash) (*ktypes.Block, ktypes.Hash, error)
BlockResultByHash(ctx context.Context, hash ktypes.Hash) ([]ktypes.TxResult, error)
ChainTx(ctx context.Context, hash ktypes.Hash) (*chaintypes.ChainTx, error)
BlockHeight(ctx context.Context) int64
ChainUnconfirmedTx(ctx context.Context, limit int) (int, []nodetypes.NamedTx)
BlockByHeight(height int64) (ktypes.Hash, *ktypes.Block, ktypes.Hash, error)
BlockByHash(hash ktypes.Hash) (*ktypes.Block, ktypes.Hash, error)
BlockResultByHash(hash ktypes.Hash) ([]ktypes.TxResult, error)
ChainTx(hash ktypes.Hash) (*chaintypes.ChainTx, error)
BlockHeight() int64
ChainUnconfirmedTx(limit int) (int, []nodetypes.NamedTx)
ConsensusParams() *ktypes.ConsensusParams
}

type Validators interface {
Expand All @@ -55,11 +56,11 @@ type Service struct {
blockchain Node // node is the local node that can accept transactions.
}

func NewService(log log.Logger, blockchain Node, genesisCfg *config.GenesisConfig) *Service {
func NewService(log log.Logger, blockchain Node, voting Validators, genesisCfg *config.GenesisConfig) *Service {
return &Service{
log: log,
genesisCfg: genesisCfg,
//voting: voting, // TODO
voting: voting,
blockchain: blockchain,
}
}
Expand Down Expand Up @@ -104,6 +105,9 @@ func (svc *Service) Methods() map[jsonrpc.Method]rpcserver.MethodDef {
chainjson.MethodGenesis: rpcserver.MakeMethodDef(svc.Genesis,
"retrieve the genesis info",
"genesis information"),
chainjson.MethodConsensusParams: rpcserver.MakeMethodDef(svc.ConsensusParams,
"retrieve the consensus parameers",
"consensus parameters"),
chainjson.MethodValidators: rpcserver.MakeMethodDef(svc.Validators,
"retrieve validator info at certain height",
"validator information at certain height"),
Expand All @@ -113,10 +117,10 @@ func (svc *Service) Methods() map[jsonrpc.Method]rpcserver.MethodDef {
}
}

func (svc *Service) HealthMethod(ctx context.Context, _ *chainjson.HealthRequest) (*chainjson.HealthResponse, *jsonrpc.Error) {
func (svc *Service) HealthMethod(_ context.Context, _ *chainjson.HealthRequest) (*chainjson.HealthResponse, *jsonrpc.Error) {
return &chainjson.HealthResponse{
ChainID: svc.genesisCfg.ChainID,
Height: svc.blockchain.BlockHeight(ctx),
Height: svc.blockchain.BlockHeight(),
Healthy: true,
}, nil
}
Expand All @@ -134,14 +138,14 @@ func verHandler(context.Context, *userjson.VersionRequest) (*userjson.VersionRes

// Block returns block information either by block height or block hash.
// If both provided, block hash will be used.
func (svc *Service) Block(ctx context.Context, req *chainjson.BlockRequest) (*chainjson.BlockResponse, *jsonrpc.Error) {
func (svc *Service) Block(_ context.Context, req *chainjson.BlockRequest) (*chainjson.BlockResponse, *jsonrpc.Error) {
if req.Height < 0 {
return nil, jsonrpc.NewError(jsonrpc.ErrorInvalidParams, "height cannot be negative", nil)
}

// prioritize req.Hash over req.Height
if !req.Hash.IsZero() {
block, appHash, err := svc.blockchain.BlockByHash(ctx, req.Hash)
block, appHash, err := svc.blockchain.BlockByHash(req.Hash)
if err != nil {
svc.log.Error("block by hash", "hash", req.Hash, "error", err)
return nil, jsonrpc.NewError(jsonrpc.ErrorNodeInternal, "failed to get block", nil)
Expand All @@ -156,7 +160,7 @@ func (svc *Service) Block(ctx context.Context, req *chainjson.BlockRequest) (*ch
}, nil
}

blockHash, block, appHash, err := svc.blockchain.BlockByHeight(ctx, req.Height)
blockHash, block, appHash, err := svc.blockchain.BlockByHeight(req.Height)
svc.log.Error("block by height", "height", req.Height, "hash", req.Hash, "error", err)
if err != nil {
return nil, jsonrpc.NewError(jsonrpc.ErrorNodeInternal, "failed to get block", nil)
Expand All @@ -173,19 +177,19 @@ func (svc *Service) Block(ctx context.Context, req *chainjson.BlockRequest) (*ch

// BlockResult returns block result either by block height or bloch hash.
// If both provided, block hash will be used.
func (svc *Service) BlockResult(ctx context.Context, req *chainjson.BlockResultRequest) (*chainjson.BlockResultResponse, *jsonrpc.Error) {
func (svc *Service) BlockResult(_ context.Context, req *chainjson.BlockResultRequest) (*chainjson.BlockResultResponse, *jsonrpc.Error) {
if req.Height < 0 {
return nil, jsonrpc.NewError(jsonrpc.ErrorInvalidParams, "height cannot be negative", nil)
}

if !req.Hash.IsZero() {
block, _, err := svc.blockchain.BlockByHash(ctx, req.Hash)
block, _, err := svc.blockchain.BlockByHash(req.Hash)
if err != nil {
svc.log.Error("block by hash", "hash", req.Hash, "error", err)
return nil, jsonrpc.NewError(jsonrpc.ErrorNodeInternal, "failed to get block: "+err.Error(), nil)
}

txResults, err := svc.blockchain.BlockResultByHash(ctx, req.Hash)
txResults, err := svc.blockchain.BlockResultByHash(req.Hash)
if err != nil {
svc.log.Error("block result by hash", "hash", req.Hash, "error", err)
return nil, jsonrpc.NewError(jsonrpc.ErrorNodeInternal, "failed to get block result: "+err.Error(), nil)
Expand All @@ -197,13 +201,13 @@ func (svc *Service) BlockResult(ctx context.Context, req *chainjson.BlockResultR
}, nil
}

blockHash, block, _, err := svc.blockchain.BlockByHeight(ctx, req.Height)
blockHash, block, _, err := svc.blockchain.BlockByHeight(req.Height)
svc.log.Error("block by height", "height", req.Height, "hash", req.Hash, "error", err)
if err != nil {
return nil, jsonrpc.NewError(jsonrpc.ErrorNodeInternal, "failed to get block", nil)
}

txResults, err := svc.blockchain.BlockResultByHash(ctx, blockHash)
txResults, err := svc.blockchain.BlockResultByHash(blockHash)
if err != nil {
svc.log.Error("block result by hash", "hash", req.Hash, "error", err)
return nil, jsonrpc.NewError(jsonrpc.ErrorNodeInternal, "failed to get block result: "+err.Error(), nil)
Expand All @@ -216,12 +220,12 @@ func (svc *Service) BlockResult(ctx context.Context, req *chainjson.BlockResultR
}

// Tx returns a transaction by hash.
func (svc *Service) Tx(ctx context.Context, req *chainjson.TxRequest) (*chaintypes.ChainTx, *jsonrpc.Error) {
func (svc *Service) Tx(_ context.Context, req *chainjson.TxRequest) (*chaintypes.ChainTx, *jsonrpc.Error) {
if req.Hash.IsZero() {
return nil, jsonrpc.NewError(jsonrpc.ErrorInvalidParams, "hash is required", nil)
}

tx, err := svc.blockchain.ChainTx(ctx, req.Hash)
tx, err := svc.blockchain.ChainTx(req.Hash)
if err != nil {
svc.log.Error("tx by hash", "hash", req.Hash, "error", err)
return nil, jsonrpc.NewError(jsonrpc.ErrorNodeInternal, "failed to get tx: "+err.Error(), nil)
Expand All @@ -230,7 +234,7 @@ func (svc *Service) Tx(ctx context.Context, req *chainjson.TxRequest) (*chaintyp
return tx, nil
}

func (svc *Service) Genesis(ctx context.Context, req *chainjson.GenesisRequest) (*chainjson.GenesisResponse, *jsonrpc.Error) {
func (svc *Service) Genesis(ctx context.Context, _ *chainjson.GenesisRequest) (*chainjson.GenesisResponse, *jsonrpc.Error) {
return &chainjson.GenesisResponse{
ChainID: svc.genesisCfg.ChainID,
Leader: svc.genesisCfg.Leader,
Expand All @@ -243,29 +247,32 @@ func (svc *Service) Genesis(ctx context.Context, req *chainjson.GenesisRequest)
}, nil
}

func (svc *Service) ConsensusParams(_ context.Context, _ *chainjson.ConsensusParamsRequest) (*chainjson.ConsensusParamsResponse, *jsonrpc.Error) {
return (*chainjson.ConsensusParamsResponse)(svc.blockchain.ConsensusParams()), nil
}

// Validators returns validator set at certain height. Default to latest height.
func (svc *Service) Validators(ctx context.Context, req *chainjson.ValidatorsRequest) (*chainjson.ValidatorsResponse, *jsonrpc.Error) {
panic("Plz inject voting dependency")
// should be able to get validator set at req.Height
//vals := svc.voting.GetValidators()
//
//pbValidators := make([]*ktypes.Validator, len(vals))
//for i, vi := range vals {
// pbValidators[i] = &ktypes.Validator{
// Role: vi.Role,
// PubKey: vi.PubKey,
// Power: vi.Power,
// }
//}
//
//return &chainjson.ValidatorsResponse{
// Height: svc.blockchain.BlockHeight(ctx),
// Validators: nil,
//}, nil
func (svc *Service) Validators(_ context.Context, _ *chainjson.ValidatorsRequest) (*chainjson.ValidatorsResponse, *jsonrpc.Error) {
// NOTE: should be able to get validator set at req.Height
vals := svc.voting.GetValidators()

pbValidators := make([]*ktypes.Validator, len(vals))
for i, vi := range vals {
pbValidators[i] = &ktypes.Validator{
Role: vi.Role,
PubKey: vi.PubKey,
Power: vi.Power,
}
}

return &chainjson.ValidatorsResponse{
Height: svc.blockchain.BlockHeight(),
Validators: nil,
}, nil
}

// UnconfirmedTxs returns the unconfirmed txs. Default return 10 txs, max return 50 txs.
func (svc *Service) UnconfirmedTxs(ctx context.Context, req *chainjson.UnconfirmedTxsRequest) (*chainjson.UnconfirmedTxsResponse, *jsonrpc.Error) {
func (svc *Service) UnconfirmedTxs(_ context.Context, req *chainjson.UnconfirmedTxsRequest) (*chainjson.UnconfirmedTxsResponse, *jsonrpc.Error) {
if req.Limit < 0 {
return nil, jsonrpc.NewError(jsonrpc.ErrorInvalidParams, "invalid limit", nil)
}
Expand All @@ -275,7 +282,7 @@ func (svc *Service) UnconfirmedTxs(ctx context.Context, req *chainjson.Unconfirm
if req.Limit == 0 {
req.Limit = 10
}
total, txs := svc.blockchain.ChainUnconfirmedTx(ctx, req.Limit)
total, txs := svc.blockchain.ChainUnconfirmedTx(req.Limit)
return &chainjson.UnconfirmedTxsResponse{
Total: total,
Txs: convertNamedTxs(txs),
Expand Down

0 comments on commit 17b1807

Please sign in to comment.