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

feat: add 'Data' field to 'respError' struct #123

Merged
merged 14 commits into from
Oct 24, 2024

Conversation

virajbhartiya
Copy link
Member

This pull request includes a small change to the handler.go file. The change adds an optional Data field to the respError struct. This is done so as to implement changes mentioned in filecoin-project/lotus#10311

  • handler.go: Added Data field to the respError struct to include additional error information.

@codecov-commenter
Copy link

codecov-commenter commented Oct 3, 2024

Codecov Report

Attention: Patch coverage is 70.42254% with 21 lines in your changes missing coverage. Please review.

Please upload report for BASE (master@c185272). Learn more about missing BASE report.

Files with missing lines Patch % Lines
handler.go 35.29% 10 Missing and 1 partial ⚠️
response.go 84.00% 6 Missing and 2 partials ⚠️
websocket.go 33.33% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##             master     #123   +/-   ##
=========================================
  Coverage          ?   71.38%           
=========================================
  Files             ?       12           
  Lines             ?     1957           
  Branches          ?        0           
=========================================
  Hits              ?     1397           
  Misses            ?      459           
  Partials          ?      101           
Files with missing lines Coverage Δ
client.go 79.67% <ø> (ø)
errors.go 65.21% <ø> (ø)
server.go 75.75% <100.00%> (ø)
websocket.go 74.41% <33.33%> (ø)
response.go 84.00% <84.00%> (ø)
handler.go 71.17% <35.29%> (ø)

@aarshkshah1992
Copy link

I think we will need more changes here for the library to determine if the error has data and to read it from the error but we can worry about it when we get blocked by it. We'll find out more when we do the Lotus work. Update Lotus to use this go-jsonrpc commit locally while you are working on the Lotus PR.

@akaladarshi
Copy link
Contributor

@aarshkshah1992 As per the @Stebalien comments : https://filecoinproject.slack.com/archives/CP50PPW2X/p1728439741305709?thread_ts=1727699414.185139&cid=CP50PPW2X

We tried to add json.RawMessage, but I was getting json body invalid in response, so I went back to using interface,
I personally haven't gone into the detail why it was happening but if you have any thought about this let us know.

@aarshkshah1992
Copy link

@akaladarshi Before I review this -> can you guys post a snippet of the errors you see for contract reverts when calling via JSON RPC on a Calibnet node ?

@akaladarshi
Copy link
Contributor

akaladarshi commented Oct 9, 2024

@akaladarshi Before I review this -> can you guys post a snippet of the errors you see for contract reverts when calling via JSON RPC on a Calibnet node ?

Screenshot 2024-10-09 at 8 54 25 PM

@aarshkshah1992 so we hardcoded the message ourselves but the data is fetched from the error returned after applying the message.

@akaladarshi
Copy link
Contributor

and for same function call ethereum returns this
Screenshot 2024-10-09 at 9 00 44 PM

errors.go Outdated
}

// DataError contains extra data to explain the error
type DataError interface {

Choose a reason for hiding this comment

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

ErrorWithData

errors.go Outdated
@@ -58,3 +58,15 @@ type marshalable interface {
json.Marshaler
json.Unmarshaler
}

// Error wraps RPC errors, which contain an error code in addition to the message.
type Error interface {

Choose a reason for hiding this comment

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

Why do we need this interface ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Added so we can have custom error codes.

Choose a reason for hiding this comment

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

Why do we need custom error codes ? Can you write a bit explaining what made you add this ? Was it something you saw during your testing ?

Choose a reason for hiding this comment

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

Also this needs a better name. This can be ErrorWithCode and the below can be ErrorWithData

Copy link
Contributor

Choose a reason for hiding this comment

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

@aarshkshah1992 I think this will give more idea towards the error codes: filecoin-project/lotus#12553

handler.go Outdated
@@ -69,6 +70,7 @@ type respError struct {
Code ErrorCode `json:"code"`
Message string `json:"message"`
Meta json.RawMessage `json:"meta,omitempty"`
Data interface{} `json:"data,omitempty"`

Choose a reason for hiding this comment

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

Why is this interface{} ?

Copy link
Contributor

Choose a reason for hiding this comment

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

@aarshkshah1992 I posted a comment above in current PR regarding this: #123 (comment)

Choose a reason for hiding this comment

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

Can we make this just a string ? Why does it have to be json.RawMessage or interface{}

@rvagg -> Any thoughts ?

Copy link
Member

Choose a reason for hiding this comment

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

https://www.jsonrpc.org/specification#error_object

A Primitive or Structured value that contains additional information about the error.

🤷 interface{} would seem more accurate, as long as it's serialisable

handler.go Outdated
}
}

var err2 Error
if errors.As(err, &err2) {
out.Code = ErrorCode(err2.ErrorCode())

Choose a reason for hiding this comment

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

Why are we setting the code here again ? Don't we already have it on line 355 above ?

Copy link
Contributor

Choose a reason for hiding this comment

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

@aarshkshah1992
So in line 355 we are setting the registered error codes, but here we are setting the custom error code.

I think we can directly register the code for ErrWithExecutionReverted, but then we need to finalise on which code to use, as error code 3 is already registered with the ActorNotFound

handler.go Outdated
@@ -504,10 +524,17 @@ func (s *handler) handle(ctx context.Context, req request, w func(func(io.Writer

log.Warnf("failed to setup channel in RPC call to '%s': %+v", req.Method, err)
stats.Record(ctx, metrics.RPCResponseError.M(1))
resp.Error = &respError{
respErr := &respError{

Choose a reason for hiding this comment

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

Don't we need to do the same thing in the rpcError function in server.go ?

Choose a reason for hiding this comment

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

Probably not as rpcErr is only used for RPC processing errors and not errors returned by the invocation of the function.

Copy link
Contributor

Choose a reason for hiding this comment

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

I am not sure about this because rpcError function is returning errors specific to RPCs (method not found etc), but the actual execution error is inside the handle function after doCall here:

callResult, err := doCall(req.Method, handler.handlerFunc, callParams)

@aarshkshah1992 aarshkshah1992 requested a review from rvagg October 15, 2024 06:43
@aarshkshah1992
Copy link

@rvagg Please can you 👁️ this ?

@rvagg
Copy link
Member

rvagg commented Oct 16, 2024

some tests for this in here would be good

response.go Outdated
var (
_ error = (*JSONRPCError)(nil)
marshalableRT = reflect.TypeOf(new(marshalable)).Elem()
unmarshalableRT = reflect.TypeOf(new(RPCErrorCodec)).Elem()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
unmarshalableRT = reflect.TypeOf(new(RPCErrorCodec)).Elem()
errorCodecRT = reflect.TypeOf(new(RPCErrorCodec)).Elem()

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

response.go Outdated
v = reflect.New(t)
}

if v.Type().Implements(unmarshalableRT) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if v.Type().Implements(unmarshalableRT) {
if v.Type().Implements(errorCodecRT) {

Copy link
Contributor

Choose a reason for hiding this comment

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

Updated.

response.go Outdated
}

if v.Type().Implements(unmarshalableRT) {
_ = v.Interface().(RPCErrorCodec).FromJSONRPCError(*e)
Copy link
Member

Choose a reason for hiding this comment

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

if we get an error here (and in the block below for UnmarshalJSON), we should:

  1. log.Warnf
  2. return reflect.ValueOf(e) because we can't know that we have a properly instantiated converted error type

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

response.go Outdated

if len(e.Meta) > 0 && v.Type().Implements(marshalableRT) {
if err := v.Interface().(marshalable).UnmarshalJSON(e.Meta); err != nil {
log.Errorf("Error unmarshaling metadata for error type '%s': %w", t.String(), err)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
log.Errorf("Error unmarshaling metadata for error type '%s': %w", t.String(), err)
log.Errorf("Error unmarshalling error metadata to custom error type '%s' (code %d): %w", t.String(), e.Code, err)

response.go Outdated

if v.Type().Implements(errorCodecRT) {
if err := v.Interface().(RPCErrorCodec).FromJSONRPCError(*e); err != nil {
log.Errorf("Error converting JSONRPCError (code %d) to custom error type '%s': %w", e.Code, t.String(), err)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
log.Errorf("Error converting JSONRPCError (code %d) to custom error type '%s': %w", e.Code, t.String(), err)
log.Errorf("Error converting JSONRPCError to custom error type '%s' (code %d): %w", t.String(), e.Code, err)

response.go Outdated
Comment on lines 74 to 76
}

if len(e.Meta) > 0 && v.Type().Implements(marshalableRT) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
}
if len(e.Meta) > 0 && v.Type().Implements(marshalableRT) {
} else if len(e.Meta) > 0 && v.Type().Implements(marshalableRT) {

let's disallow dual behaviour for now, we could enable this at some point if there was ever a case for it (I can't think what it would be) but best avoid surprises if we're not testing for it

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

handler.go Outdated
Comment on lines 507 to 467
resp.Error = &respError{

respErr := &JSONRPCError{
Code: 1,
Message: err.Error(),
}

if m, ok := err.(RPCErrorCodec); ok {
rpcErr, err := m.ToJSONRPCError()
if err != nil {
log.Warnf("Failed to convert error to JSONRPCError: %v", err)
} else {
respErr.Data = rpcErr.Data
}
}

resp.Error = respErr
Copy link
Member

Choose a reason for hiding this comment

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

This is unnecessary, we only ever get back an internal error at this point which is why code 1 is being used as the internal error code (custom codes start at 2). You can follow this case down in to websocket.go handleChanOut. So all you need to do here is s/respError/JSONRPCError/ and leave the rest alone.

Copy link
Contributor

Choose a reason for hiding this comment

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

I will remove this from here, wdym by when you say You can follow this case down in to websocket.go handleChanOut. I don't see any error in the response there.

handler.go Outdated
case RPCErrorCodec:
o, err := m.ToJSONRPCError()
if err != nil {
log.Warnf("Failed to convert error to JSONRPCError: %v", err)
Copy link
Member

Choose a reason for hiding this comment

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

let's Errorf these two as well, if they're not converting then it's either a programmer error or a system error (like OOM)

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.

Copy link
Member

@rvagg rvagg left a comment

Choose a reason for hiding this comment

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

looks good, just a few minor changes and this is good to ship I think

@rvagg rvagg merged commit a9fac20 into filecoin-project:master Oct 24, 2024
7 checks passed
@rvagg
Copy link
Member

rvagg commented Oct 24, 2024

well done @virajbhartiya & @akaladarshi, nice work on this one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ☑️ Done (Archive)
Development

Successfully merging this pull request may close these issues.

5 participants