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: change error&error codes returned from /transaction/* related endpoints #1312

Merged
merged 3 commits into from
Aug 14, 2023

Conversation

Imod7
Copy link
Contributor

@Imod7 Imod7 commented Aug 7, 2023

Relates: #1311

Description

In the transaction related endpoints :

  • /transaction
  • /transaction/dry-run
  • /transaction/fee-estimate

the errors are handled :

  1. by using the extractCauseAndStack method (to extract the cause and stack of the error)
  2. and then throw an error, e.g. in TransactionSubmitService.ts, with the above info.
  3. which then passes through the txErrorMiddleware.ts file and throws a hardcoded 500 error here (also hardcoded in the info here)

With the changes in the current PR, we are now handling the errors in the following way :

  1. still using the extractCauseAndStack method
  2. and then we again throw an error but with some differences :
    1. now we set the error code in the service file instead of the txErrorMiddleware.ts file
    2. and now the error code that we set is 400 instead of 500
  3. then this error is passed through the txErrorMiddleware.ts file again but now with the error code set already.

This change was mentioned in the #1311 issue and it was concerning only the /transaction endpoint. However, I think it also fits in the rest of the transaction related endpoints since the logic is the same, meaning If we decide to throw a 400 error if a tx fails to be submitted then we should throw the same error if a tx fails to be dry-run. And also if we fail to retrieve the fee info.

Return Errors & Error Codes

Example Returned Error before the change

{
  "code": 500,
  "error": "Failed to parse transaction.",
  "transaction": "0x250284d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01022f4deae1532ddd01b88c4897151447ecfad96e18cf91088716a29ed00a6f514c56612f92650f66df1719a04935ce0b9c23b07ed81ae038844e33ee733bd588a501000005008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4830",
  "cause": "createType(ExtrinsicV4):: createType(ExtrinsicSignatureV4):: decodeU8aStruct: failed at 0xd43593c715fdd31c61141abd04a99fd6… on signer (index 1/5): {\"_enum\":{\"Id\":\"AccountId\",\"Index\":\"Compact<AccountIndex>\",\"Raw\":\"Bytes\",\"Address32\":\"H256\",\"Address20\":\"H160\"}}:: Unable to create Enum via index 212, in Id, Index, Raw, Address32, Address20",
  "stack": "Error: createType(ExtrinsicV4):: createType(ExtrinsicSignatureV4):: decodeU8aStruct: failed at 0xd43593c715fdd31c61141abd04a99fd6… on signer (index 1/5): {\"_enum\":{\"Id\":\"AccountId\",\"Index\":\"Compact<AccountIndex>\",\"Raw\":\"Bytes\",\"Address32\":\"H256\",\"Address20\":\"H160\"}}:: Unable to create Enum via index 212, in Id, Index, Raw, Address32, Address20\n    at createTypeUnsafe (/Users/dominique/Documents/Parity/substrate-api-sidecar/node_modules/@polkadot/types-create/cjs/create/type.js:54:22)\n    at TypeRegistry.createTypeUnsafe (/Users/dominique/Documents/Parity/substrate-api-sidecar/node_modules/@polkadot/types/cjs/create/registry.js:271:52)\n    at newFromValue (/Users/dominique/Documents/Parity/substrate-api-sidecar/node_modules/@polkadot/types/cjs/extrinsic/Extrinsic.js:25:21)\n    at decodeU8a (/Users/dominique/Documents/Parity/substrate-api-sidecar/node_modules/@polkadot/types/cjs/extrinsic/Extrinsic.js:48:12)\n    at decodeExtrinsic (/Users/dominique/Documents/Parity/substrate-api-sidecar/node_modules/@polkadot/types/cjs/extrinsic/Extrinsic.js:30:16)\n    at new GenericExtrinsic (/Users/dominique/Documents/Parity/substrate-api-sidecar/node_modules/@polkadot/types/cjs/extrinsic/Extrinsic.js:184:25)\n    at new Submittable (/Users/dominique/Documents/Parity/substrate-api-sidecar/node_modules/@polkadot/api/cjs/submittable/createClass.js:53:13)\n    at ApiPromise._extrinsics (/Users/dominique/Documents/Parity/substrate-api-sidecar/node_modules/@polkadot/api/cjs/submittable/createSubmittable.js:7:27)\n    at TransactionSubmitService.submitTransaction (/Users/dominique/Documents/Parity/substrate-api-sidecar/build/src/services/transaction/TransactionSubmitService.js:32:22)\n    at TransactionSubmitController.txSubmit (/Users/dominique/Documents/Parity/substrate-api-sidecar/build/src/controllers/transaction/TransactionSubmitController.js:55:41)"
}

Example Returned Error after the change

 {
  "code": 400,
  "error": "Failed to parse transaction.",
  "transaction": "0xa101846c029e6fc41ec44d420030071f04995bac19e59a0f0a1a610f9f0f6d689e2262016c85c8b3f10b5442fe3162ed140f9b0fd865d7452c4bb96196932e940579cd6e1576d8360a2b895f2072bedc6cde66e1c0a67326d9d5883793a3a2baa131868f85011c001f00",
  "cause": "createType(ExtrinsicV4):: createType(ExtrinsicSignatureV4):: decodeU8aStruct: failed at 0x6c029e6fc41ec44d420030071f04995b… on signer (index 1/5): {\"_enum\":{\"Id\":\"AccountId\",\"Index\":\"Compact<AccountIndex>\",\"Raw\":\"Bytes\",\"Address32\":\"H256\",\"Address20\":\"H160\"}}:: Unable to create Enum via index 108, in Id, Index, Raw, Address32, Address20",
  "stack": "Error: createType(ExtrinsicV4):: createType(ExtrinsicSignatureV4):: decodeU8aStruct: failed at 0x6c029e6fc41ec44d420030071f04995b… on signer (index 1/5): {\"_enum\":{\"Id\":\"AccountId\",\"Index\":\"Compact<AccountIndex>\",\"Raw\":\"Bytes\",\"Address32\":\"H256\",\"Address20\":\"H160\"}}:: Unable to create Enum via index 108, in Id, Index, Raw, Address32, Address20\n    at createTypeUnsafe (/Users/dominique/Documents/Parity/sidecar-tx-code/node_modules/@polkadot/types-create/cjs/create/type.js:54:22)\n    at TypeRegistry.createTypeUnsafe (/Users/dominique/Documents/Parity/sidecar-tx-code/node_modules/@polkadot/types/cjs/create/registry.js:271:52)\n    at newFromValue (/Users/dominique/Documents/Parity/sidecar-tx-code/node_modules/@polkadot/types/cjs/extrinsic/Extrinsic.js:25:21)\n    at decodeU8a (/Users/dominique/Documents/Parity/sidecar-tx-code/node_modules/@polkadot/types/cjs/extrinsic/Extrinsic.js:48:12)\n    at decodeExtrinsic (/Users/dominique/Documents/Parity/sidecar-tx-code/node_modules/@polkadot/types/cjs/extrinsic/Extrinsic.js:30:16)\n    at new GenericExtrinsic (/Users/dominique/Documents/Parity/sidecar-tx-code/node_modules/@polkadot/types/cjs/extrinsic/Extrinsic.js:184:25)\n    at new Submittable (/Users/dominique/Documents/Parity/sidecar-tx-code/node_modules/@polkadot/api/cjs/submittable/createClass.js:53:13)\n    at ApiPromise._extrinsics (/Users/dominique/Documents/Parity/sidecar-tx-code/node_modules/@polkadot/api/cjs/submittable/createSubmittable.js:7:27)\n    at TransactionSubmitService.submitTransaction (/Users/dominique/Documents/Parity/sidecar-tx-code/build/src/services/transaction/TransactionSubmitService.js:31:22)\n    at TransactionSubmitController.txSubmit (/Users/dominique/Documents/Parity/sidecar-tx-code/build/src/controllers/transaction/TransactionSubmitController.js:55:41)"
}

Returned Errors Comparison

So with this change :

  • we are throwing the correct error code (400 instead of 500) and
  • we keep all the information returned about the error (that is very helpful for debugging) :
    • code
    • error
    • transaction
    • cause
    • stack

Example Request that will/should trigger a 400 error

curl -X POST "http://0.0.0.0:8080/transaction/" \
        -H  "accept: application/json" \
        -H "Content-Type: application/json" \
        -d '{"tx": "0xa101846c029e6fc41ec44d420030071f04995bac19e59a0f0a1a610f9f0f6d689e2262016c85c8b3f10b5442fe3162ed140f9b0fd865d7452c4bb96196932e940579cd6e1576d8360a2b895f2072bedc6cde66e1c0a67326d9d5883793a3a2baa131868f85011c001f00"}'

while ofc a sidecar instance (checked out in the current branch) is running locally.

Credits to @TarikGul and @marshacb for the very insightful observations/reviews on this PR so we can choose the best solution/implementation.

@Imod7 Imod7 requested a review from a team as a code owner August 7, 2023 10:32
cause,
stack,
};
throw new BadRequest('Failed to parse transaction.');
Copy link
Collaborator

@marshacb marshacb Aug 7, 2023

Choose a reason for hiding this comment

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

is there a way we can continue including/returning the transaction/stack information as part of the error? This info can be useful when looking into specific issues/debugging.

Copy link
Member

Choose a reason for hiding this comment

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

Plus one to this, otherwise it looks really great!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes exactly! 💯 Thank you both for pointing this out 🙏 I forgot to mention it in the PR description.
I tried to find a way to keep the stack and cause from extractCauseAndStack for this exact reason that you mention but I was still getting a 500 error at the end. I will check some more to see how I can override the StatusCode returned. Thank YOU both for your time on reviewing ! 🙏

Copy link
Contributor Author

@Imod7 Imod7 Aug 9, 2023

Choose a reason for hiding this comment

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

Thank you @TarikGul and @marshacb 🙏 to :

  • point out the fact that we prefer keeping the debugging info and
  • point in the direction of the middleware files for the error handling

Based on those sugegstions, I removed the BadRequest solution (and also did not push the CustomBadRequest solution which I was planning) and opted for the solution of passing the status code in the txErrorMiddleware.
This implied the following changes :

  • Reverted to the previous error handling with extractCauseAndStack
  • Added the code in err and passing it in the txErrorMiddleware
  • Reverted the changes to the test .spec files
  • Updated the mock files with the error code returned since now err also includes the code
  • Updated ITxLegacyError
  • Reverted changes in docs and rebuilt

Important Note

  • I am not setting a default value for code in the txErrorMiddleware so if it is not set in the service file or from wherever it is called then it will result in a 500 status code and it will output a not so pretty error: Invalid status code: undefined. Maybe we would like to handle this more gracefully ?

  • TODO from my side : Update the description of this PR to reflect the final implementation.

Copy link
Collaborator

@marshacb marshacb left a comment

Choose a reason for hiding this comment

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

Great work on this! Just a small question about the info returned in the error

…rrorMiddleware`

- Reverted to the previous error handling with `extractCauseAndStack`
- Added the `code` in error thrown and passed it in the `txErrorMiddleware`
- Reverted the changes to the test `.spec` files
- Updated the mock files with the error code returned since now `err` also includes the `code`
- Updated `ITxLegacyError`
- Reverted changes in docs and rebuilt
@@ -22,6 +22,7 @@ export function extractCauseAndStack(err: unknown): {
cause: string | unknown;
stack: string | undefined;
} {
// const code = err instanceof Error ? err : '';
Copy link
Member

Choose a reason for hiding this comment

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

Just need to either remove or handle this commented out code.

Copy link
Member

@TarikGul TarikGul left a comment

Choose a reason for hiding this comment

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

Really amazing job! Lgtm

Copy link
Collaborator

@marshacb marshacb left a comment

Choose a reason for hiding this comment

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

LGTM!

@Imod7 Imod7 merged commit 797c421 into master Aug 14, 2023
13 checks passed
@Imod7 Imod7 deleted the domi-tx-error-codes branch August 14, 2023 14:21
This pull request was closed.
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.

3 participants