Skip to content

Commit

Permalink
fix: change call rpc payload to bytes
Browse files Browse the repository at this point in the history
Due to serialization issue, client(sdk) and server may have a different view of the data to be signed.
Use bytes as payload make sure all parties use same data to sign/verify.
  • Loading branch information
Yaiba authored and jchappelow committed Feb 26, 2024
1 parent aa763d4 commit 6abcffe
Show file tree
Hide file tree
Showing 13 changed files with 54 additions and 123 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -33,4 +33,4 @@ Invoke `task` command to see all available tasks.
task docker:kwild
# run docker container
task dev:up
```
```
32 changes: 2 additions & 30 deletions api/openapi-spec/api/v1/api.swagger.json
Expand Up @@ -389,28 +389,12 @@
}
}
},
"txCallPayload": {
"type": "object",
"properties": {
"dbid": {
"type": "string"
},
"action": {
"type": "string"
},
"args": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/txScalarValue"
}
}
}
},
"txCallRequest": {
"type": "object",
"properties": {
"payload": {
"$ref": "#/definitions/txCallPayload"
"type": "string",
"format": "byte"
},
"signature": {
"$ref": "#/definitions/txSignature"
Expand Down Expand Up @@ -572,18 +556,6 @@
}
}
},
"txScalarValue": {
"type": "object",
"properties": {
"stringValue": {
"type": "string"
},
"intValue": {
"type": "string",
"format": "int64"
}
}
},
"txSignature": {
"type": "object",
"properties": {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -20,7 +20,7 @@ require (
github.com/jpillora/backoff v1.0.0
github.com/kwilteam/action-grammar-go v0.0.0-20230626155541-72265621f427
github.com/kwilteam/go-sqlite v0.0.0-20230606000142-c7eaa7111421
github.com/kwilteam/kuneiform v0.4.2-0.20230727223543-9936fe66d639
github.com/kwilteam/kuneiform v0.4.2-0.20230803190249-fa8a902b3465
github.com/kwilteam/kwil-extensions v0.0.0-20230710163303-bfa03f64ff82
github.com/kwilteam/sql-grammar-go v0.0.2
github.com/manifoldco/promptui v0.9.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -383,8 +383,8 @@ github.com/kwilteam/action-grammar-go v0.0.0-20230626155541-72265621f427 h1:MYJu
github.com/kwilteam/action-grammar-go v0.0.0-20230626155541-72265621f427/go.mod h1:2KYz1ittMk7BbKRJSnSnRSysrgDUUtNB1Ha47ZMdc6M=
github.com/kwilteam/go-sqlite v0.0.0-20230606000142-c7eaa7111421 h1:TewJpDtkIU8ZsIoWMqTcWmZKZS6i4AeuBylgogQ1dRI=
github.com/kwilteam/go-sqlite v0.0.0-20230606000142-c7eaa7111421/go.mod h1:urRZ5yExms/OcYQHq0IAPLkNoudEbfUuQdlNvhcfrKI=
github.com/kwilteam/kuneiform v0.4.2-0.20230727223543-9936fe66d639 h1:SWb/dGbivDmD0zLUZY7n3pd/7ckQHtJNvzjk/uz/fGA=
github.com/kwilteam/kuneiform v0.4.2-0.20230727223543-9936fe66d639/go.mod h1:nml7f8UA4MQbbV7bcoek8AkgjMXC/X9Ie2Mt4RGKYoQ=
github.com/kwilteam/kuneiform v0.4.2-0.20230803190249-fa8a902b3465 h1:U5SOkvAsm9CavR5NFCV2uNS3uFxE6mAIOe59xpCgLhI=
github.com/kwilteam/kuneiform v0.4.2-0.20230803190249-fa8a902b3465/go.mod h1:nml7f8UA4MQbbV7bcoek8AkgjMXC/X9Ie2Mt4RGKYoQ=
github.com/kwilteam/kuneiform-grammar-go v0.4.1-0.20230727221325-6535cb9a21f9 h1:VYqLGUAP68gQg6qpvU0A3kl/LySfa4lJMbnIuaPn82s=
github.com/kwilteam/kuneiform-grammar-go v0.4.1-0.20230727221325-6535cb9a21f9/go.mod h1:uki0OHLCXttIQqy/DsJ5X9WaNtRf3HB/OmxCCd+XdZU=
github.com/kwilteam/kwil-extensions v0.0.0-20230710163303-bfa03f64ff82 h1:pA0ya2WrncGSxxXB0g3dVq1jZZSm1HO6Qp0/yYn4qks=
Expand Down
37 changes: 14 additions & 23 deletions internal/controller/grpc/txsvc/v1/call.go
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"reflect"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand All @@ -15,6 +14,7 @@ import (
)

func (s *Service) Call(ctx context.Context, req *txpb.CallRequest) (*txpb.CallResponse, error) {

execBody, err := convertActionCall(req)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to convert action call: %s", err.Error())
Expand Down Expand Up @@ -43,38 +43,29 @@ func (s *Service) Call(ctx context.Context, req *txpb.CallRequest) (*txpb.CallRe
}, nil
}

func convertActionCall(req *txpb.CallRequest) (*entity.ActionCall, error) {
convertedParams := make(map[string]interface{})
for k, v := range req.Payload.Args {
var anyVal any

switch realVal := v.Value.(type) {
case *txpb.ScalarValue_IntValue:
anyVal = realVal.IntValue
case *txpb.ScalarValue_StringValue:
anyVal = realVal.StringValue
default:
return nil, status.Errorf(codes.InvalidArgument, "unknown value type '%s' for param %s", reflect.TypeOf(v).String(), k)
}

convertedParams[k] = anyVal
func convertActionCall(req *txpb.CallRequest) (*entity.CallAction, error) {
var actionPayload *tx.CallActionPayload
err := json.Unmarshal(req.Payload, &actionPayload)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to unmarshal payload: %s", err.Error())
}

convSignature, err := convertSignature(req.Signature)
if err != nil {
return nil, err
}

exec := &entity.ActionCall{
Message: &tx.SignedMessage[*tx.CallActionPayload]{
Payload: &tx.CallActionPayload{
Action: req.Payload.Action,
DBID: req.Payload.Dbid,
Params: convertedParams,
},
exec := &entity.CallAction{
Message: &tx.SignedMessage[tx.JsonPayload]{
Payload: tx.JsonPayload(req.Payload),
Signature: convSignature,
Sender: req.Sender,
},
Payload: &tx.CallActionPayload{
Action: actionPayload.Action,
DBID: actionPayload.DBID,
Params: actionPayload.Params,
},
}

return exec, nil
Expand Down
4 changes: 2 additions & 2 deletions internal/controller/grpc/txsvc/v1/pricing.go
Expand Up @@ -76,8 +76,8 @@ func (s *Service) priceAction(ctx context.Context, tx *kTx.Transaction) (*big.In
})
}

func unmarshalActionExecution(payload []byte) (*entity.ActionExecution, error) {
exec := entity.ActionExecution{}
func unmarshalActionExecution(payload []byte) (*kTx.ExecuteActionPayload, error) {
exec := kTx.ExecuteActionPayload{}

err := json.Unmarshal(payload, &exec)
if err != nil {
Expand Down
15 changes: 5 additions & 10 deletions internal/entity/execution.go
Expand Up @@ -6,17 +6,12 @@ import (

type ExecuteAction struct {
Tx *tx.Transaction
ExecutionBody *ActionExecution
ExecutionBody *tx.ExecuteActionPayload
}

type ActionExecution struct {
Action string `json:"action"`
DBID string `json:"dbid"`
Params []map[string]any `json:"params"`
}

// ActionCall is a struct that represents the action call
// CallAction is a struct that represents the action call
// a call is a read-only action
type ActionCall struct {
Message *tx.SignedMessage[*tx.CallActionPayload]
type CallAction struct {
Message *tx.SignedMessage[tx.JsonPayload]
Payload *tx.CallActionPayload
}
6 changes: 3 additions & 3 deletions internal/usecases/datasets/call.go
Expand Up @@ -7,13 +7,13 @@ import (
"github.com/kwilteam/kwil-db/pkg/engine/dataset"
)

func (u *DatasetUseCase) Call(ctx context.Context, action *entity.ActionCall) ([]map[string]any, error) {
ds, err := u.engine.GetDataset(ctx, action.Message.Payload.DBID)
func (u *DatasetUseCase) Call(ctx context.Context, action *entity.CallAction) ([]map[string]any, error) {
ds, err := u.engine.GetDataset(ctx, action.Payload.DBID)
if err != nil {
return nil, err
}

return ds.Call(ctx, action.Message.Payload.Action, action.Message.Payload.Params, &dataset.TxOpts{
return ds.Call(ctx, action.Payload.Action, action.Payload.Params, &dataset.TxOpts{
Caller: action.Message.Sender,
})
}
2 changes: 1 addition & 1 deletion internal/usecases/datasets/interfaces.go
Expand Up @@ -42,7 +42,7 @@ type DatasetUseCaseInterface interface {
GetSchema(context.Context, string) (*entity.Schema, error)

// Call calls a read-only action on a database
Call(ctx context.Context, action *entity.ActionCall) ([]map[string]any, error)
Call(ctx context.Context, action *entity.CallAction) ([]map[string]any, error)
}

type AccountStore interface {
Expand Down
47 changes: 3 additions & 44 deletions pkg/grpc/client/v1/call.go
Expand Up @@ -4,27 +4,19 @@ import (
"context"
"encoding/json"
"fmt"
"reflect"

"github.com/cstockton/go-conv"
txpb "github.com/kwilteam/kwil-db/api/protobuf/tx/v1"
"github.com/kwilteam/kwil-db/pkg/crypto"
"github.com/kwilteam/kwil-db/pkg/tx"
)

func (c *Client) Call(ctx context.Context, req *tx.CallActionMessage) ([]map[string]any, error) {

scalarMap, err := paramsToScalar(req.Payload.Params)
payload, err := req.Payload.Bytes()
if err != nil {
return nil, fmt.Errorf("failed to convert params to scalar: %w", err)
return nil, fmt.Errorf("failed to convert payload to bytes: %w", err)
}

grpcMsg := &txpb.CallRequest{
Payload: &txpb.CallPayload{
Dbid: req.Payload.DBID,
Action: req.Payload.Action,
Args: scalarMap,
},
Payload: payload,
Signature: convertActionSignature(req.Signature),
Sender: req.Sender,
}
Expand All @@ -44,39 +36,6 @@ func (c *Client) Call(ctx context.Context, req *tx.CallActionMessage) ([]map[str
return result, nil
}

func paramsToScalar(oldParams map[string]any) (map[string]*txpb.ScalarValue, error) {
newParams := make(map[string]*txpb.ScalarValue)
for k, v := range oldParams {

var scalarVal *txpb.ScalarValue
switch concreteVal := v.(type) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, bool:
int64Val, err := conv.Int64(concreteVal)
if err != nil {
return nil, fmt.Errorf("failed to convert int value: %w", err)
}

scalarVal = &txpb.ScalarValue{
Value: &txpb.ScalarValue_IntValue{
IntValue: int64Val,
},
}
case string:
scalarVal = &txpb.ScalarValue{
Value: &txpb.ScalarValue_StringValue{
StringValue: concreteVal,
},
}
default:
return nil, fmt.Errorf("unknown value type '%s' for param %s", reflect.TypeOf(v).String(), k)
}

newParams[k] = scalarVal
}

return newParams, nil
}

func convertActionSignature(oldSig *crypto.Signature) *txpb.Signature {
if oldSig == nil {
return &txpb.Signature{}
Expand Down
17 changes: 17 additions & 0 deletions pkg/tx/message.go
Expand Up @@ -20,6 +20,10 @@ type Serializable interface {
Bytes() ([]byte, error)
}

type Verifiable interface {
Verify() error
}

func (s *SignedMessage[T]) generateHash() ([]byte, error) {
data, err := s.Payload.Bytes()
if err != nil {
Expand Down Expand Up @@ -82,3 +86,16 @@ type CallActionPayload struct {
func (c *CallActionPayload) Bytes() ([]byte, error) {
return json.Marshal(c)
}

type JsonPayload []byte

func (j JsonPayload) Bytes() ([]byte, error) {
return j, nil
}

// ExecuteActionPayload is a struct that represents the action execution
type ExecuteActionPayload struct {
Action string `json:"action"`
DBID string `json:"dbid"`
Params []map[string]any `json:"params"`
}
7 changes: 2 additions & 5 deletions pkg/tx/tx.go
Expand Up @@ -41,12 +41,9 @@ func (t *Transaction) Verify() error {
}

// Not returning this function directly since I want specific error messages.
ok, err := kwilCrypto.CheckSignature(t.Sender, t.Signature, t.Hash)
err := t.Signature.Check(t.Sender, t.Hash)
if err != nil {
return fmt.Errorf("unexpected error checking signature: %v", err)
}
if !ok {
return fmt.Errorf("invalid signature")
return fmt.Errorf("failed to verify signed message: %v", err)
}

return nil
Expand Down
2 changes: 1 addition & 1 deletion proto
Submodule proto updated 1 files
+1 −14 kwil/tx/v1/call.proto

0 comments on commit 6abcffe

Please sign in to comment.