Skip to content

Commit

Permalink
PRT-allow-cache-batches-and-string-ids-in-json (#978)
Browse files Browse the repository at this point in the history
* fix cache not allowing jsonrpc batches or string ids

* fixed all issues
  • Loading branch information
omerlavanet authored Nov 20, 2023
1 parent 69254dc commit 12f6f8c
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 18 deletions.
6 changes: 3 additions & 3 deletions ecosystem/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"math/rand"
"strconv"
"testing"
"time"

Expand Down Expand Up @@ -481,9 +482,8 @@ func TestCacheSetGetJsonRPCWithID(t *testing.T) {
if tt.valid {
require.Nil(t, err)
result := gjson.GetBytes(cacheReply.GetReply().Data, format.IDFieldName)
require.Equal(t, gjson.Number, result.Type)
extractedID := result.Int()
require.Equal(t, changedID, extractedID)
extractedID := result.Raw
require.Equal(t, strconv.FormatInt(changedID, 10), extractedID)
} else {
require.Error(t, err)
}
Expand Down
17 changes: 17 additions & 0 deletions ecosystem/cache/format/formatter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package format

import (
"testing"

spectypes "github.com/lavanet/lava/x/spec/types"
"github.com/stretchr/testify/require"
)

func TestFormatter(t *testing.T) {
inputFormatter, outputFormatter := FormatterForRelayRequestAndResponse(spectypes.APIInterfaceJsonRPC)
data := `[{"jsonrpc":"2.0","id":1,"method":"eth_chainId"},{"jsonrpc":"2.0","id":3,"method":"eth_chainId"}]`
inputData := inputFormatter([]byte(data))
require.NotEqual(t, string(inputData), data)
outData := outputFormatter(inputData)
require.Equal(t, string(outData), data)
}
77 changes: 62 additions & 15 deletions ecosystem/cache/format/jsonrpc.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package format

import (
"encoding/json"

"github.com/lavanet/lava/utils"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
Expand All @@ -14,22 +16,36 @@ const (
// removing ID from the request so we have cache matches when id is changing
func FormatterForRelayRequestAndResponseJsonRPC() (inputFormatter func([]byte) []byte, outputFormatter func([]byte) []byte) {
// extractedID is used in outputFormatter to return the id that the user requested
var extractedID int64 = -1
var extractedID interface{} = "-1"
extractedIDArray := []interface{}{}
inputFormatter = func(inpData []byte) []byte {
if len(inpData) < 3 {
return inpData
}

result := gjson.GetBytes(inpData, IDFieldName)
if result.Type != gjson.Number {
// something is wrong with the id field, its not a number
_ = utils.LavaFormatError("invalid field type for id in json formatter", nil, utils.Attribute{Key: "jsonData", Value: inpData})
return inpData
batch := []json.RawMessage{}
err := json.Unmarshal(inpData, &batch)
if err == nil && len(batch) > 1 {
modifiedInpArray := []json.RawMessage{}
for _, batchData := range batch {
var extractedIDForBatch interface{}
var modifiedInp []byte
modifiedInp, extractedIDForBatch, err = getExtractedIdAndModifyInputForJson(batchData)
if err != nil {
return inpData
}
extractedIDArray = append(extractedIDArray, extractedIDForBatch)
modifiedInpArray = append(modifiedInpArray, modifiedInp)
}
modifiedOut, err := json.Marshal(modifiedInpArray)
if err != nil {
utils.LavaFormatError("failed to marshal batch", err)
return inpData
}
return modifiedOut
}
extractedID = result.Int()
modifiedInp, err := sjson.SetBytes(inpData, IDFieldName, DefaultIDValue)
var modifiedInp []byte
modifiedInp, extractedID, err = getExtractedIdAndModifyInputForJson(inpData)
if err != nil {
_ = utils.LavaFormatError("failed to set id 1 in json", nil, utils.Attribute{Key: "jsonData", Value: inpData})
return inpData
}
return modifiedInp
Expand All @@ -38,17 +54,48 @@ func FormatterForRelayRequestAndResponseJsonRPC() (inputFormatter func([]byte) [
if len(inpData) == 0 {
return inpData
}

result := gjson.GetBytes(inpData, IDFieldName)
if result.Type != gjson.Number {
// something is wrong with the id field, its not a number
return inpData
batch := []json.RawMessage{}
err := json.Unmarshal(inpData, &batch)
if err == nil && len(batch) > 1 && len(extractedIDArray) == len(batch) {
modifiedInpArray := []json.RawMessage{}
for i, batchData := range batch {
modifiedInp, err := sjson.SetBytes(batchData, IDFieldName, extractedIDArray[i])
if err != nil {
utils.LavaFormatWarning("failed to set id in batch cache", err)
return inpData
}
modifiedInpArray = append(modifiedInpArray, modifiedInp)
}
modifiedOut, err := json.Marshal(modifiedInpArray)
if err != nil {
utils.LavaFormatError("failed to marshal batch", err)
return inpData
}
return modifiedOut
}
modifiedInp, err := sjson.SetBytes(inpData, IDFieldName, extractedID)
if err != nil {
utils.LavaFormatWarning("failed to set input id in cache", err)
return inpData
}
return modifiedInp
}
return inputFormatter, outputFormatter
}

func getExtractedIdAndModifyInputForJson(inpData []byte) (modifiedInp []byte, extractedID interface{}, err error) {
result := gjson.GetBytes(inpData, IDFieldName)
switch result.Type {
case gjson.Number:
extractedID = result.Int()
case gjson.String:
extractedID = result.Raw
default:
extractedID = result.Value()
}
modifiedInp, err = sjson.SetBytes(inpData, IDFieldName, DefaultIDValue)
if err != nil {
return inpData, extractedID, utils.LavaFormatWarning("failed to set id in json", nil, utils.Attribute{Key: "jsonData", Value: inpData}, utils.LogAttr("extractedID", extractedID))
}
return modifiedInp, extractedID, nil
}

0 comments on commit 12f6f8c

Please sign in to comment.