Skip to content

Commit

Permalink
feature: jsonrpc client for function service
Browse files Browse the repository at this point in the history
  • Loading branch information
Yaiba committed May 29, 2024
1 parent 040e7cf commit b9947d4
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 14 deletions.
3 changes: 3 additions & 0 deletions core/rpc/client/function/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package function

import (
"context"
"errors"

"github.com/kwilteam/kwil-db/core/crypto/auth"
)

type FunctionServiceClient interface {
VerifySignature(ctx context.Context, sender []byte, signature *auth.Signature, message []byte) error
}

var ErrInvalidSignature = errors.New("invalid signature")
10 changes: 5 additions & 5 deletions core/rpc/client/function/http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@ package http
import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/http"
"net/url"
"strings"

"github.com/kwilteam/kwil-db/core/crypto/auth"
"github.com/kwilteam/kwil-db/core/rpc/client/function"
httpFunction "github.com/kwilteam/kwil-db/core/rpc/http/function"
)

var ErrInvalidSignature = errors.New("invalid signature")

type Client struct {
conn *httpFunction.APIClient
url *url.URL
Expand Down Expand Up @@ -45,6 +43,8 @@ func NewClient(target *url.URL, opts ...ClientOption) *Client {
return c
}

var _ function.FunctionServiceClient = (*Client)(nil)

func (c *Client) VerifySignature(ctx context.Context, sender []byte, signature *auth.Signature, message []byte) error {
result, res, err := c.conn.FunctionServiceApi.FunctionServiceVerifySignature(ctx, httpFunction.FunctionVerifySignatureRequest{
Sender: base64.StdEncoding.EncodeToString(sender),
Expand All @@ -61,14 +61,14 @@ func (c *Client) VerifySignature(ctx context.Context, sender []byte, signature *

// server logic error
if result.Error_ != "" {
return fmt.Errorf("%w: %s", ErrInvalidSignature, result.Error_)
return fmt.Errorf("%w: %s", function.ErrInvalidSignature, result.Error_)
}

// NOTE: Forget why I put both `valid` and `error` in the response.
// if `valid` is false, `error` should not be empty.
// This might be not needed, but I just keep it here.
if !result.Valid {
return ErrInvalidSignature
return function.ErrInvalidSignature
}

return nil
Expand Down
64 changes: 64 additions & 0 deletions core/rpc/client/function/jsonrpc/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// package jsonrpc implements a JSON-RPC client for the Kwil function service.

package jsonrpc

import (
"context"
"fmt"
"net/url"

"github.com/kwilteam/kwil-db/core/crypto/auth"
rpcclient "github.com/kwilteam/kwil-db/core/rpc/client"
"github.com/kwilteam/kwil-db/core/rpc/client/function"
jsonFunction "github.com/kwilteam/kwil-db/core/rpc/json/function"
)

// Client is a JSON-RPC client for the Kwil function service. It uses the
// JSONRPCClient from the rpcclient package for the actual JSON-RPC communication,
// and implements function service methods.
type Client struct {
*rpcclient.JSONRPCClient
}

// NewClient creates a new json rpc client, target should be the base URL
// of the provider server, and should not include "/rpc/v1" as that is appended
// automatically.
// NOTE: No error will be returned.
func NewClient(target *url.URL, opts ...rpcclient.RPCClientOpts) *Client {
g := &Client{
JSONRPCClient: rpcclient.NewJSONRPCClient(target, opts...),
}

return g
}

var _ function.FunctionServiceClient = (*Client)(nil)

func (c *Client) VerifySignature(ctx context.Context, sender []byte, signature *auth.Signature, message []byte) error {
result := &jsonFunction.VerifySignatureResponse{}
err := c.CallMethod(ctx, string(jsonFunction.MethodVerifySig), &jsonFunction.VerifySignatureRequest{
Sender: sender,
Msg: message,
Signature: &jsonFunction.TxSignature{
SignatureBytes: signature.Signature,
SignatureType: signature.Type,
},
}, result)

if err != nil { // protocol/communication level error
return err
}

if result.Reason != "" {
return fmt.Errorf("%w: %s", function.ErrInvalidSignature, result.Reason)
}

// NOTE: Forget why I put both `valid` and `error` in the response.
// if `valid` is false, `error` should not be empty.
// This might be not needed, but I just keep it here.
if !result.Valid {
return function.ErrInvalidSignature
}

return nil
}
7 changes: 4 additions & 3 deletions core/rpc/client/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/kwilteam/kwil-db/core/crypto/auth"
jsonrpc "github.com/kwilteam/kwil-db/core/rpc/json"
"github.com/kwilteam/kwil-db/core/types"
)

Expand Down Expand Up @@ -45,7 +46,7 @@ type AuthnParameterResponse struct {
}

const (
MethodAuthn = "kgw.authn"
MethodAuthnParam = "kgw.authn_param"
//MethodLogout = "kgw.logout"
MethodAuthn jsonrpc.Method = "kgw.authn"
MethodAuthnParam jsonrpc.Method = "kgw.authn_param"
//MethodLogout jsonrpc.Method = "kgw.logout"
)
7 changes: 2 additions & 5 deletions core/rpc/client/gateway/jsonrpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ type Client struct {
// of the gateway server, and should not include "/rpc/v1" as that is appended
// automatically. If the client does not have a cookie jar, an error is returned.
func NewClient(target *url.URL, opts ...gateway.ClientOption) (*Client, error) {
// This client uses API v1 methods and request/response types.
target = target.JoinPath("/rpc/v1")

c := gateway.DefaultClientOptions()
c.JSONRPCClient = rpcclient.NewJSONRPCClient(target)
for _, o := range opts {
Expand All @@ -53,13 +50,13 @@ var _ gateway.Client = (*Client)(nil)
// It sets the returned cookie in the client's cookie jar.
func (g *Client) Authn(ctx context.Context, auth *gateway.AuthnRequest) error {
res := &gateway.AuthnResponse{}
err := g.CallMethod(ctx, gateway.MethodAuthn, auth, res)
err := g.CallMethod(ctx, string(gateway.MethodAuthn), auth, res)
return err
}

// GetAuthnParameter returns the auth parameter for the client.
func (g *Client) GetAuthnParameter(ctx context.Context) (*gateway.AuthnParameterResponse, error) {
res := &gateway.AuthnParameterResponse{}
err := g.CallMethod(ctx, gateway.MethodAuthnParam, &gateway.AuthnParameterRequest{}, res)
err := g.CallMethod(ctx, string(gateway.MethodAuthnParam), &gateway.AuthnParameterRequest{}, res)
return res, err
}
3 changes: 3 additions & 0 deletions core/rpc/client/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ func (cl *JSONRPCClient) nextReqID() string {

// NOTE: make a BaseClient with CallMethod only.

// CallMethod makes a JSON-RPC request to the server. The method is the name of
// the method to call, cmd is the request parameter, and res is the response object.
// If the server returns a jsonrpc.Error, it's a business logic error.
func (cl *JSONRPCClient) CallMethod(ctx context.Context, method string, cmd, res any) error {
// res needs to be a pointer otherwise we can't unmarshal into it.
if rtp := reflect.TypeOf(res); rtp.Kind() != reflect.Ptr {
Expand Down
2 changes: 1 addition & 1 deletion internal/services/jsonrpc/funcsvc/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (svc *Service) Handlers() map[jsonrpc.Method]rpcserver.MethodHandler {
req := &jsonrpc.VersionRequest{}
return req, func() (any, *jsonrpc.Error) {
return &jsonrpc.VersionResponse{
Service: "user",
Service: "function",
Version: apiSemver,
Major: apiVerMajor,
Minor: apiVerMinor,
Expand Down

0 comments on commit b9947d4

Please sign in to comment.