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

fix: add accept: application/json header to remote signer requests and improve invalid response handling #6587

Merged
merged 23 commits into from Apr 4, 2024

Conversation

cnupy
Copy link
Contributor

@cnupy cnupy commented Mar 24, 2024

Motivation

Some Remote Signing Service implementations do not return by default response in application/json format and the signing fails.

Description

This pull request modifies externalSignerClient.ts to add the accept: application/json request header, instructing the remote signer to return the response as json.

Steps to test or reproduce

Reproducible using Diva DVT as remote signer.

@cnupy cnupy requested a review from a team as a code owner March 24, 2024 17:33
@CLAassistant
Copy link

CLAassistant commented Mar 24, 2024

CLA assistant check
All committers have signed the CLA.

@cnupy cnupy changed the title Handle remote signer non application/json response content type correctly Handle remote signer non application/json response content type Mar 24, 2024
@cnupy cnupy changed the title Handle remote signer non application/json response content type fix: handle remote signer non application/json response content type Mar 24, 2024
@cnupy cnupy changed the title fix: handle remote signer non application/json response content type fix: handle remote signer non "application/json" response content type Mar 24, 2024
Copy link

codecov bot commented Mar 24, 2024

Codecov Report

Merging #6587 (facf88c) into unstable (3076b4c) will decrease coverage by 0.02%.
The diff coverage is 13.33%.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #6587      +/-   ##
============================================
- Coverage     61.70%   61.69%   -0.02%     
============================================
  Files           556      556              
  Lines         58799    58820      +21     
  Branches       1886     1887       +1     
============================================
+ Hits          36283    36287       +4     
- Misses        22475    22492      +17     
  Partials         41       41              

Copy link
Member

@nflaig nflaig left a comment

Choose a reason for hiding this comment

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

Thanks @cnupy for looking into this, let's try to get this in our next release

I think there needs to be a general handler for text vs json response, this logic is basically the same across all three functions.

I think it is ok right now to have a fix we can include for Diva but long term we need to use a declarative API client as we do for other APIs in @lodestar/api package and test it against the spec.

packages/cli/src/cmds/validator/signers/index.ts Outdated Show resolved Hide resolved
packages/validator/src/util/externalSignerClient.ts Outdated Show resolved Hide resolved
packages/validator/src/util/externalSignerClient.ts Outdated Show resolved Hide resolved
packages/validator/src/util/externalSignerClient.ts Outdated Show resolved Hide resolved
packages/validator/src/util/externalSignerClient.ts Outdated Show resolved Hide resolved
packages/validator/src/util/externalSignerClient.ts Outdated Show resolved Hide resolved
@nflaig
Copy link
Member

nflaig commented Mar 27, 2024

It would also good to make sure we test against Diva in our CI, there are already generic signer tests here

describe("web3signer signature test", function () {

We would just have to extend this function

export async function startExternalSigner({

I can't tell how complex it is to run Diva like this, but would be awesome to test against

@nflaig nflaig added this to the v1.18.0 milestone Mar 27, 2024
@cnupy
Copy link
Contributor Author

cnupy commented Mar 27, 2024

Thanks @cnupy for looking into this, let's try to get this in our next release

I think there needs to be a general handler for text vs json response, this logic is basically the same across all three functions.

I think it is ok right now to have a fix we can include for Diva but long term we need to use a declarative API client as we do for other APIs in @lodestar/api package and test it against the spec.

Thanks for the feedback. This is actually my very first time writing anything in TypeScript, so please excuse me if I've messed up a few things. I will do my best to address all comments for now, and will be more than happy to help with the declarative API client in the near future (once I get more familiar with the codebase).

@nflaig
Copy link
Member

nflaig commented Mar 27, 2024

This is actually my very first time writing anything in TypeScript, so please excuse me if I've messed up a few things

Don't worry about that, this is a great PR to improve compatibility with Diva and also follow the spec, as we should have been supporting text responses already. Happy to assist on the PR to get it merged, and feel free to join our discord and ask any questions there as well

will be more than happy to help with the declarative API client in the near future (once I get more familiar with the codebase).

I am currently working on a major rework of the API client, it doesn't consider text responses right now as a wire format but could extend in the future. Happy to collaborate on that as well once it is ready

…move error logging and improve exception messaging for res != OK; fix content type parsing and handling of invalid content types;
@cnupy
Copy link
Contributor Author

cnupy commented Mar 28, 2024

It would also good to make sure we test against Diva in our CI, there are already generic signer tests here

describe("web3signer signature test", function () {

We would just have to extend this function

export async function startExternalSigner({

I can't tell how complex it is to run Diva like this, but would be awesome to test against

It shouldn't be hard to instantiate a Diva node container, but the actual testing might be harder than consensys/web3signer, as Diva is a distributed P2P remote signer and I'm not sure how to get it to sign a test message or return a test key store. Perhaps someone from Diva could help here. Generic tests against a pre-prepared response payloads might be easier to get the job done for now.

@nflaig
Copy link
Member

nflaig commented Mar 28, 2024

but the actual testing might be harder than consensys/web3signer, as Diva is a distributed P2P remote signer and I'm not sure how to get it to sign a test message or return a test key store

Unless they have some sort of standalone or dev mode it will probably be hard based on how it is configured here. I have asked in their Discord if there is a simpler setup.

@cnupy
Copy link
Contributor Author

cnupy commented Mar 29, 2024

but the actual testing might be harder than consensys/web3signer, as Diva is a distributed P2P remote signer and I'm not sure how to get it to sign a test message or return a test key store

Unless they have some sort of standalone or dev mode it will probably be hard based on how it is configured here. I have asked in their Discord if there is a simpler setup.

Not much cooperation in Diva DVT discord, and their node/remotesigner is closed source for now. I guess not much can be done about that for now.

@nflaig
Copy link
Member

nflaig commented Mar 29, 2024

Not much cooperation in Diva DVT discord, and their node/remotesigner is closed source for now.

I got a response but this is not possible in current release, they noted something about next release they might include it.

Based on just this PR it feels like there is definitley some work that Diva has to do, e.g. return errors if something fails. It doesn't seem like it would be stable right now to include in our CI.

I am ok with including a fix as long as we make sure web3signer is not impacted which we actively test against so should be fine there.

@nflaig
Copy link
Member

nflaig commented Mar 29, 2024

OK, is there anything else I need to do?

Existing tests are passing so I think we are good on that front, if you have confirmed that Diva compatibility is fixed as well I think we can go ahead and merge this.

Will do another review round locally, but this PR should be good to include in our next release.

Thanks @cnupy for fast updates and helping us to be compatible with Diva :)

@cnupy
Copy link
Contributor Author

cnupy commented Mar 29, 2024

OK, is there anything else I need to do?

Existing tests are passing so I think we are good on that front, if you have confirmed that Diva compatibility is fixed as well I think we can go ahead and merge this.

Will do another review round locally, but this PR should be good to include in our next release.

Thanks @cnupy for fast updates and helping us to be compatible with Diva :)

I can confirm that I can successfully sign messages and submit attestations using Diva as a remote signer on Holesky. I'm still getting Precondition Failed (412) errors on some requests, but this is probably related to the DVT cluster consensus. I also get Invalid aggregate and proof errors from the beacon node from time to time, but I don't think this is related to this PR.

Thanks @nflaig for your support :)

Copy link
Member

@nflaig nflaig left a comment

Choose a reason for hiding this comment

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

I was talking to the Diva team about the response format, and they already support json responses for all APIs as per spec but currently default to text/plain if no Accept header is set. But mentioned this default behavior will be changed in the next release.

The main concern I have with the changes in this PR is that adding text/plain support will make it harder to reuse the generic api client for the remote signer in the future. And to be spec compliant we also don't really need to implement handling text responses as we can just state in the Accept header that we only accept json. It is only required by the server to support both content type responses.

I would suggest the following

  • make sure we set accept: application/json in all requests
  • explicitly validate content-type header in response and throw good error message if header is missing or format is unsupported (instead of some weird parsing errors)
  • include the error improvements to fall back to res.statusText if res.text() is empty

… error handling; add accept header to all requests;
@cnupy cnupy changed the title fix: handle remote signer non "application/json" response content type fix: add accept: application/json header to remote signer requests and improve invalid response handling Apr 4, 2024
@cnupy
Copy link
Contributor Author

cnupy commented Apr 4, 2024

  • make sure we set accept: application/json in all requests
  • explicitly validate content-type header in response and throw good error message if header is missing or format is unsupported (instead of some weird parsing errors)
  • include the error improvements to fall back to res.statusText if res.text() is empty

All done and works as expected.

@cnupy cnupy requested a review from nflaig April 4, 2024 08:04
@nflaig
Copy link
Member

nflaig commented Apr 4, 2024

All done and works as expected.

Awesome, thanks for the fast update. I pushed some minor updates but those shouldn't change anything. You can review and approve then we should be good to merge this

Copy link
Contributor Author

@cnupy cnupy left a comment

Choose a reason for hiding this comment

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

Great. Thanks for correcting that.

@nflaig
Copy link
Member

nflaig commented Apr 4, 2024

Great. Thanks for correcting that.

woops, I broke json parsing but fixed now. Want to do some more debugging locally against web3signer to see how we handle those errors etc.

If you make sure we work with Diva we are good to go 🚀

return JSON.parse(await res.text()) as T;
function getErrorMessage(errBody: string): string {
try {
const errJson = JSON.parse(errBody) as {message: string};
Copy link
Member

Choose a reason for hiding this comment

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

No idea why neither Web3Signer nor Diva return proper errors as json but I made this a bit more future proof to handle potential json errors.

The spec does not define an error schema so we can't be sure how remote signers implement this.

Copy link
Contributor Author

@cnupy cnupy Apr 4, 2024

Choose a reason for hiding this comment

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

I don't think it's a good idea to treat error response as json if it's not in the spec. Even if the error response is actually json, you are not guaranteed that there is a field named message (it could be msg, error or even errorMsg ...) and the result will be worse than if you had included the response content as plain text. You may also need to check the content-type of the error response before treating it as json. I know there is a try/catch block, but why do this if it's not in the spec?

Copy link
Member

Choose a reason for hiding this comment

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

Ok fair, we would have to check content type and response format is unclear. Just assumed sane format similar to beacon api. Although for errors is not as critical to check proper format and have fallback logic via try/catch.

Let's keep error handling as is, the Diva team is working on the spec will ask them about it and see if we can standardize this a bit better.

Copy link
Member

@nflaig nflaig left a comment

Choose a reason for hiding this comment

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

LGTM, thanks again @cnupy for the fast response cycles

@nflaig nflaig changed the title fix: add accept: application/json header to remote signer requests and improve invalid response handling fix: set accept header in remote signer requests and improve invalid response handling Apr 4, 2024
@nflaig nflaig changed the title fix: set accept header in remote signer requests and improve invalid response handling fix: add accept: application/json header to remote signer requests and improve invalid response handling Apr 4, 2024
@nflaig nflaig changed the title fix: add accept: application/json header to remote signer requests and improve invalid response handling fix: add accept: application/json header to remote signer requests and improve invalid response handling Apr 4, 2024
@jeluard jeluard self-requested a review April 4, 2024 14:42
@nflaig nflaig merged commit fd6691d into ChainSafe:unstable Apr 4, 2024
22 of 23 checks passed
@wemeetagain
Copy link
Member

🎉 This PR is included in v1.18.0 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants