Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf!: Changed tuplelist deserialization. #76

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions pkg/castor/client.go
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update Copyright header

Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@
package castor

import (
"encoding/json"
"errors"
"fmt"
"github.com/google/uuid"
"io/ioutil"
"net/http"
"net/url"
"strconv"

"github.com/google/uuid"

"github.com/asaskevich/govalidator"
)

// AbstractClient is an interface for castor tuple client.
type AbstractClient interface {
GetTuples(tupleCount int32, tupleType TupleType, requestID uuid.UUID) (*TupleList, error)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is TupleList still used at all?

GetTuples(tupleCount int32, tupleType TupleType, requestID uuid.UUID) ([]byte, error)
}

// NewClient returns a new Castor client for the given endpoint
Expand All @@ -48,7 +48,7 @@ const countParam = "count"
const reservationIDParam = "reservationId"

// GetTuples retrieves a list of tuples matching the given criteria from Castor
func (c *Client) GetTuples(count int32, tt TupleType, requestID uuid.UUID) (*TupleList, error) {
func (c *Client) GetTuples(count int32, tt TupleType, requestID uuid.UUID) ([]byte, error) {
values := url.Values{}
values.Add(tupleTypeParam, tt.Name)
values.Add(countParam, strconv.Itoa(int(count)))
Expand All @@ -66,17 +66,16 @@ func (c *Client) GetTuples(count int32, tt TupleType, requestID uuid.UUID) (*Tup
if err != nil {
return nil, fmt.Errorf("communication with castor failed: %s", err)
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as the byte[] to is later written to a pipe (file) consumed by the MPC runtime, wouldn't it make sense to return the Body (ReadCloser) directly. This would eliminate the intermediate step of "chaching" as a byte[] first.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possibly as an additional asStream / asReader method

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one could utilize io.Copy for implementation

if resp.StatusCode != http.StatusOK {
bodyBytes, err := ioutil.ReadAll(resp.Body)

if err != nil {
return nil, err
}
return nil, fmt.Errorf("getting tuples failed for \"%s\" with response code #%d: %s", req.URL, resp.StatusCode, string(bodyBytes))
}
tuples := &TupleList{}
err = json.NewDecoder(resp.Body).Decode(tuples)
if err != nil {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's unnecessary code now, isn't it?

return nil, fmt.Errorf("castor has returned an invalid response body: %s", err)
}
return tuples, nil
return bodyBytes, nil
}
19 changes: 4 additions & 15 deletions pkg/castor/client_test.go
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update Copyright header

Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@ var _ = Describe("Castor", func() {
})
Context("when the path is correct", func() {
It("returns tuples", func() {
mockedRT := MockedRoundTripper{ExpectedPath: "/intra-vcp/tuples", ReturnJSON: jsn, ExpectedResponseCode: http.StatusOK}
tbytelist := []byte{1, 2, 1, 2}
mockedRT := MockedRoundTripper{ExpectedPath: "/intra-vcp/tuples", ReturnJSON: tbytelist, ExpectedResponseCode: http.StatusOK}
httpClient := &http.Client{Transport: &mockedRT}

client := Client{URL: myURL, HTTPClient: httpClient}
tuples, err := client.GetTuples(0, BitGfp, uuid.MustParse("acc23dc8-7855-4a2f-bc89-494ba30a74d2"))
tuples, err := client.GetTuples(2, BitGfp, uuid.MustParse("acc23dc8-7855-4a2f-bc89-494ba30a74d2"))

Expect(tuples).To(Equal(tupleList))
Expect(tuples).To(Equal(tbytelist))
Expect(err).NotTo(HaveOccurred())
})
})
Expand All @@ -90,18 +91,6 @@ var _ = Describe("Castor", func() {
Expect(checkHTTPError(err.Error(), "communication with castor failed")).To(BeTrue())
})
})
Context("when castor returns invalid json body", func() {
It("returns an error", func() {
jsn = []byte("invalid JSON String")
mockedRT := MockedRoundTripper{ExpectedPath: "/intra-vcp/tuples", ReturnJSON: jsn, ExpectedResponseCode: http.StatusOK}
httpClient := &http.Client{Transport: &mockedRT}

client := Client{URL: myURL, HTTPClient: httpClient}
_, err := client.GetTuples(0, BitGfp, uuid.MustParse("acc23dc8-7855-4a2f-bc89-494ba30a74d2"))

Expect(checkHTTPError(err.Error(), "castor has returned an invalid response body")).To(BeTrue())
})
})

})

Expand Down
27 changes: 1 addition & 26 deletions pkg/ephemeral/io/tuple_streamer.go
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update Copyright header

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
package io

import (
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
Expand Down Expand Up @@ -273,15 +272,11 @@ func (ts *CastorTupleStreamer) bufferData(terminateCh chan struct{}, streamerErr
func (ts *CastorTupleStreamer) getTupleData() ([]byte, error) {
requestID := uuid.NewMD5(ts.baseRequestID, []byte(strconv.Itoa(ts.requestCycle)))
ts.requestCycle++
tupleList, err := ts.castorClient.GetTuples(ts.stockSize, ts.tupleType, requestID)
tupleData, err := ts.castorClient.GetTuples(ts.stockSize, ts.tupleType, requestID)
if err != nil {
return nil, err
}
ts.logger.Debugw("Fetched new tuples from Castor", "RequestID", requestID)
tupleData, err := ts.tupleListToByteArray(tupleList)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should at least check whether received data fits to the requested tuple parameters (length of array equals number of requested tuples x length of requested tuple type, or sth like that)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must be part of the client if GetTuples returns a Reader as proposed above

if err != nil {
return nil, fmt.Errorf("error parsing received tuple list: %v", err)
}
return tupleData, nil
}

Expand Down Expand Up @@ -328,26 +323,6 @@ func (ts *CastorTupleStreamer) writeDataToPipe(terminateCh chan struct{}, doneCh
}
}

// tupleListToByteArray converts a given list of tuple to a byte array
func (ts *CastorTupleStreamer) tupleListToByteArray(tl *castor.TupleList) ([]byte, error) {
var result []byte
for _, tuple := range tl.Tuples {
for _, share := range tuple.Shares {
decodeString, err := base64.StdEncoding.DecodeString(share.Value)
if err != nil {
return []byte{}, err
}
result = append(result, decodeString...)
decodeString, err = base64.StdEncoding.DecodeString(share.Mac)
if err != nil {
return []byte{}, err
}
result = append(result, decodeString...)
}
}
return result, nil
}

// generateHeader returns the file header for the given protocol and spdz runtime configuration
func generateHeader(sp castor.SPDZProtocol, conf *SPDZEngineTypedConfig) ([]byte, error) {
switch sp {
Expand Down
57 changes: 7 additions & 50 deletions pkg/ephemeral/io/tuple_streamer_test.go
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update Copyright header

Original file line number Diff line number Diff line change
Expand Up @@ -105,49 +105,6 @@ var _ = Describe("Tuple Streamer", func() {
Expect(<-errCh).NotTo(BeNil())
})
})
Context("when castor client returns illegal data", func() {
Context("when share value is invalid", func() {
It("writes error to error channel and stops", func() {
invalidShareValue := "value"
share := castor.Share{Value: invalidShareValue}
shares := make([]castor.Share, 1)
shares[0] = share
tuples := make([]castor.Tuple, 1)
tuples[0] = castor.Tuple{Shares: shares}
cc.TupleList = &castor.TupleList{Tuples: tuples}
wg.Add(1)
ts.StartStreamTuples(terminate, errCh, wg)
wg.Wait()
close(terminate)
close(errCh)
Expect(fcpw.isClosed).To(BeTrue())
err := <-errCh
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(errors.New("error parsing received tuple list: illegal base64 data at input byte 4")))
})
})
Context("when share value is invalid", func() {
It("writes error to error channel and stops", func() {
validShareValue := base64.StdEncoding.EncodeToString([]byte("value"))
invalidShareMac := "value"
share := castor.Share{Value: validShareValue, Mac: invalidShareMac}
shares := make([]castor.Share, 1)
shares[0] = share
tuples := make([]castor.Tuple, 1)
tuples[0] = castor.Tuple{Shares: shares}
cc.TupleList = &castor.TupleList{Tuples: tuples}
wg.Add(1)
ts.StartStreamTuples(terminate, errCh, wg)
wg.Wait()
close(terminate)
close(errCh)
Expect(fcpw.isClosed).To(BeTrue())
err := <-errCh
Expect(err).To(HaveOccurred())
Expect(err).To(Equal(errors.New("error parsing received tuple list: illegal base64 data at input byte 4")))
})
})
})
Context("when tuples fetched successfully", func() {
shareValue := "value"
shareMac := "value"
Expand All @@ -161,7 +118,7 @@ var _ = Describe("Tuple Streamer", func() {
tuples[0] = castor.Tuple{Shares: shares}
initialStreamData := []byte(shareValue + shareMac)
BeforeEach(func() {
cc.TupleList = &castor.TupleList{Tuples: tuples}
cc.TupleData = initialStreamData
})
Context("when writing data to pipe fails with broken pipe", func() {
var expectedError = syscall.EPIPE
Expand Down Expand Up @@ -551,19 +508,19 @@ func (fpcfpw *FakePartialConsumingFailSecondCallPipeWriter) Close() error {
}

type FakeCastorClient struct {
TupleList *castor.TupleList
TupleData []byte
}

func (fcc *FakeCastorClient) GetTuples(int32, castor.TupleType, uuid.UUID) (*castor.TupleList, error) {
tl := fcc.TupleList
func (fcc *FakeCastorClient) GetTuples(int32, castor.TupleType, uuid.UUID) ([]byte, error) {
tl := fcc.TupleData
if tl == nil {
tl = &castor.TupleList{}
tl = []byte{}
}
return tl, nil
}

type BrokenDownloadCastorClient struct{}

func (fcc *BrokenDownloadCastorClient) GetTuples(int32, castor.TupleType, uuid.UUID) (*castor.TupleList, error) {
return &castor.TupleList{}, errors.New("fetching tuples failed")
func (fcc *BrokenDownloadCastorClient) GetTuples(int32, castor.TupleType, uuid.UUID) ([]byte, error) {
return []byte{}, errors.New("fetching tuples failed")
}
Loading