oracle: implement filters

This commit is contained in:
Evgeniy Stratonikov 2021-01-22 17:32:29 +03:00
parent e4528e59dc
commit c420014cb5
6 changed files with 113 additions and 0 deletions

1
go.mod
View file

@ -1,6 +1,7 @@
module github.com/nspcc-dev/neo-go module github.com/nspcc-dev/neo-go
require ( require (
github.com/PaesslerAG/jsonpath v0.1.1
github.com/Workiva/go-datastructures v1.0.50 github.com/Workiva/go-datastructures v1.0.50
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db
github.com/alicebob/miniredis v2.5.0+incompatible github.com/alicebob/miniredis v2.5.0+incompatible

5
go.sum
View file

@ -8,6 +8,11 @@ github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PaesslerAG/gval v1.0.0 h1:GEKnRwkWDdf9dOmKcNrar9EA1bz1z9DqPIO1+iLzhd8=
github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I=
github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=
github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk=
github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY=
github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo= github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo=
github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw= github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=

View file

@ -140,6 +140,10 @@ func TestOracle(t *testing.T) {
putOracleRequest(t, cs.Hash, bc, "http://get.maxallowed", nil, "handle", []byte{}, 10_000_000) putOracleRequest(t, cs.Hash, bc, "http://get.maxallowed", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "http://get.maxallowed", nil, "handle", []byte{}, 100_000_000) putOracleRequest(t, cs.Hash, bc, "http://get.maxallowed", nil, "handle", []byte{}, 100_000_000)
flt := "Values[1]"
putOracleRequest(t, cs.Hash, bc, "http://get.filter", &flt, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "http://get.filterinv", &flt, "handle", []byte{}, 10_000_000)
checkResp := func(t *testing.T, id uint64, resp *transaction.OracleResponse) *state.OracleRequest { checkResp := func(t *testing.T, id uint64, resp *transaction.OracleResponse) *state.OracleRequest {
req, err := oracleCtr.GetRequestInternal(bc.dao, id) req, err := oracleCtr.GetRequestInternal(bc.dao, id)
require.NoError(t, err) require.NoError(t, err)
@ -242,6 +246,19 @@ func TestOracle(t *testing.T) {
Result: make([]byte, transaction.MaxOracleResultSize), Result: make([]byte, transaction.MaxOracleResultSize),
}) })
}) })
t.Run("WithFilter", func(t *testing.T) {
checkResp(t, 10, &transaction.OracleResponse{
ID: 10,
Code: transaction.Success,
Result: []byte(`[2]`),
})
t.Run("invalid response", func(t *testing.T) {
checkResp(t, 11, &transaction.OracleResponse{
ID: 11,
Code: transaction.Error,
})
})
})
} }
func TestOracleFull(t *testing.T) { func TestOracleFull(t *testing.T) {
@ -354,6 +371,14 @@ func newDefaultHTTPClient() oracle.HTTPClient {
code: http.StatusOK, code: http.StatusOK,
body: make([]byte, transaction.MaxOracleResultSize), body: make([]byte, transaction.MaxOracleResultSize),
}, },
"http://get.filter": {
code: http.StatusOK,
body: []byte(`{"Values":["one", 2, 3],"Another":null}`),
},
"http://get.filterinv": {
code: http.StatusOK,
body: []byte{0xFF},
},
}, },
} }
} }

View file

@ -0,0 +1,24 @@
package oracle
import (
"encoding/json"
"errors"
"unicode/utf8"
"github.com/PaesslerAG/jsonpath"
)
func filter(value []byte, path string) ([]byte, error) {
if !utf8.Valid(value) {
return nil, errors.New("not an UTF-8")
}
var v interface{}
if err := json.Unmarshal(value, &v); err != nil {
return nil, err
}
result, err := jsonpath.Get(path, v)
if err != nil {
return nil, err
}
return json.Marshal([]interface{}{result})
}

View file

@ -0,0 +1,50 @@
package oracle
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFilter(t *testing.T) {
js := `{
"Stores": [ "Lambton Quay", "Willis Street" ],
"Manufacturers": [
{
"Name": "Acme Co",
"Products": [
{ "Name": "Anvil", "Price": 50 }
]
},
{
"Name": "Contoso",
"Products": [
{ "Name": "Elbow Grease", "Price": 99.95 },
{ "Name": "Headlight Fluid", "Price": 4 }
]
}
]
}`
testCases := []struct {
result, path string
}{
{`["Acme Co"]`, "Manufacturers[0].Name"},
{`[50]`, "Manufacturers[0].Products[0].Price"},
{`["Elbow Grease"]`, "Manufacturers[1].Products[0].Name"},
{`[{"Name":"Elbow Grease","Price":99.95}]`, "Manufacturers[1].Products[0]"},
}
for _, tc := range testCases {
t.Run(tc.path, func(t *testing.T) {
actual, err := filter([]byte(js), tc.path)
require.NoError(t, err)
require.Equal(t, tc.result, string(actual))
})
}
t.Run("not an UTF-8", func(t *testing.T) {
_, err := filter([]byte{0xFF}, "Manufacturers[0].Name")
require.Error(t, err)
})
}

View file

@ -115,6 +115,14 @@ func (o *Oracle) processRequest(priv *keys.PrivateKey, req request) error {
} }
break break
} }
if req.Req.Filter != nil {
res, err := filter(result, *req.Req.Filter)
if err != nil {
resp.Code = transaction.Error
break
}
result = res
}
resp.Code = transaction.Success resp.Code = transaction.Success
resp.Result = result resp.Result = result
case r.StatusCode == http.StatusForbidden: case r.StatusCode == http.StatusForbidden: