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

Add preview, submit tx and tx stream APIs in Rust #95

Merged
merged 11 commits into from
Aug 30, 2022

Conversation

LukasGasior1
Copy link
Contributor

No description provided.

@LukasGasior1 LukasGasior1 changed the base branch from feature/openapi-rust to main August 25, 2022 16:24
@LukasGasior1 LukasGasior1 changed the base branch from main to feature/openapi-rust August 25, 2022 16:24
Base automatically changed from feature/openapi-rust to main August 26, 2022 12:58
@LukasGasior1 LukasGasior1 marked this pull request as ready for review August 26, 2022 12:59
state-manager/core-api-server/core-api-spec.yaml Outdated Show resolved Hide resolved
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
'500':
Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO it's quite pointless to specify 500 responses, just focus on responses returned by application that serve business purposes (200 and 400 in this case).
500, 501, 502, 503, 401, 402, 100, ... - if customer to our API is not expected to face them not to mention handle them what's the point of documenting them? BTW we can't guarantee response payload format for 500 errors :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, I don't see much point in specifying it either. As a side note, I'll also get rid of the leftover polymorphic error types (InvalidTransactionError, PreviewError etc) - it isn't supported by the Rust generator.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmh, I take that back, it seems that with the Rust openapi gen it's required to define the 500 error responses if we want to return any meaningful messages for server errors. Otherwise we can't return a custom message (it's hardcoded to a generic "An internal error occurred").

Overall, there are 4 categories of error responses:

  • a request that doesn't match the schema: 400 + txt error message (no json) - handled by the generated server
  • client error (or any error that can loosely fit into this category, e.g. "mempool full" error) - 400 + json error (code, message) - corresponds to Ok(SomeResponse::ClientError)
  • expected/handled server error (e.g. state manager lock error) - 500 + json error - corresponds to Ok(SomeResponse::ServerError)
  • unhandled server error - 500 + generic txt error message - corresponds to Err(ApiError(...)) - we shouldn't really use it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could be that "unhandled server error - 500 + generic txt error message" is also returned by the generated server in some cases

Copy link
Contributor

@dhedey dhedey Aug 29, 2022

Choose a reason for hiding this comment

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

@KrzysztofLabus-RDX - just for context, the Core API (like the Olympia Gateway) was designed to follow "RPC over HTTP" so everything which isn't a 200 is a 500; with a separate internal code.

I'd be potentially happy to change this for Babylon, but it'd need a wider conversation.

Copy link
Contributor

Choose a reason for hiding this comment

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

@dhedey Ah, right! Forget the question/suggestion then :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"everything which isn't a 200 is a 500;" is that really the case? I thought that'd be "everything which isn't a 200 is either a 400 or a 500;"

Copy link
Contributor

Choose a reason for hiding this comment

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

Honestly, I'm happy to use proper HTTP response codes. But paging @talekhinezh in case he wants to argue for keeping 500 for everything

items:
type: string
error_message:
type: string
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we please define optional (non-required) properties as nullable? There's long reasoning behind that: https://radixdlt.atlassian.net/browse/NG-64?jql=text%20~%20%22nullable%20json%22 tl;dr: changes nothing in practice but makes it possible to define proper backing types (think: POJOs) in virtually any sane language (Java, C#, PHP etc.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup, can do

Copy link
Contributor Author

@LukasGasior1 LukasGasior1 Aug 26, 2022

Choose a reason for hiding this comment

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

This, on the other hand, kind-of breaks the Rust generation :D Non-required nullable fields become Option<Nullable<T>> which isn't ideal, but I guess we can live with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

btw, did you just call PHP a sane language!?

Copy link
Contributor

@krzlabrdx krzlabrdx Aug 29, 2022

Choose a reason for hiding this comment

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

I believe you're using this OAS Generator to automatically generate backing types, right? So it looks like it's capable of producing proper backing types with use of Ruts' Option. I'll take my request back. What you've been doing was just right. I'll simply try to fix C# OAS Generator generator to produce nullable resource where appropriate. Ideally we could improve it to the point where C# generator also uses Optional<T> where T is nullable or non-nullable type but that would be more difficult due to various reasons. CC @dhedey

EDIT: Yeah, I've called PHP sane language, shame on me. :D

Copy link
Contributor

@dhedey dhedey Aug 29, 2022

Choose a reason for hiding this comment

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

If we're not supporting nullable types in the Core API, then, yes my PR here documents two distinct issues with the handling of required/not-required types in the C# generation: OpenAPITools/openapi-generator#11607 - and fixes issue (2) - but hasn't been merged. I think we're talking about issue (1) here.

It's quite important in the C# that both (1) and (2) are fixed in the generated models - or at least it was for Olympia.

I'd possibly recommend either picking up the PR, and/or fixing these in template file overrides locally - see eg https://github.com/radixdlt/radixdlt-python-clients/tree/main/regeneration and https://openapi-generator.tech/docs/templating

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, removed "nullable"

Copy link
Contributor

@dhedey dhedey 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! A few suggestions for minor tweaks. Excited to see this speed us up compared to having this in Java.

Is the plan to rename /state-manager/ to eg /rust (or something) in a small PR at some point? :)

- notary_signature
properties:
hash:
type: string
Copy link
Contributor

Choose a reason for hiding this comment

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

In Olympia, Josh designed the API to return explicit wrapped "identifier" objects, I'd be happy to ditch these though - but just wanted to check this was an intentional direction?

Copy link
Contributor

Choose a reason for hiding this comment

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

We could also make these a $ref to a string type with docs, in order to de-dupe any docs we want to add :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was more accidental than intentional, I kind of like the idea of having those defined as separate types

notary_as_signatory:
type: boolean
cost_unit_limit:
type: string
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we document what these mean / represent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do 👍

@@ -363,16 +457,13 @@ components:
type: string
cost_unit_limit:
description: A limit of cost units available for execution
Copy link
Contributor

Choose a reason for hiding this comment

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

For these three options, can we mention in the documentation that these are integers?

I know in Olympia for integers, we used format: integer etc, and reserved strings for big integers, or longs where we're likely to go about 2^53 (cf https://stackoverflow.com/users/596689/tom-christie ).

But also potentially happy to just use strings if we feel this is more consistent; but I'd like us to ensure we are consistent, and to document this somewhere. (Inline in each explanation; and the general approach and justification in the overall API description).

Copy link
Contributor Author

@LukasGasior1 LukasGasior1 Aug 29, 2022

Choose a reason for hiding this comment

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

good point, wdyt about adding custom types (and use $ref) for UInt*?

Copy link
Contributor Author

@LukasGasior1 LukasGasior1 Aug 29, 2022

Choose a reason for hiding this comment

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

ah, but that might not work well with descriptions

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah - sadly this doesn't work so well with descriptions :( - otherwise I love it.

.lock()
.map_err(|_| submit_server_error("Internal server error (state manager lock)"))?;

let result = locked_state_manager.mempool.add_transaction(transaction);
Copy link
Contributor

Choose a reason for hiding this comment

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

It's worth us flagging to @talekhinezh that this doesn't go through any code in the Java Ledger/StateManager, suggesting anything like metrics / locking / error handling should move down into the Rust mempool eventually 👍

Copy link
Member

Choose a reason for hiding this comment

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

Yup this is fine.

TransactionSubmitPostResponse::ServerError(ErrorResponse::new(500, message.to_string()))
}

pub(crate) fn handle_transactions(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we name this slightly more clearly? eg handle_get_committed_transactions

let mut txns = vec![];
let mut state_version = initial_state_version;
while state_version <= up_to_state_version_inclusive {
if let Some(next_tid) = locked_state_manager.proof_store.get_tid(state_version) {
Copy link
Contributor

Choose a reason for hiding this comment

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

When would we expect state version to be missing from the state store?

What do you think about us (A) reading some "max state version", (B) capping up_to_state_version_inclusive to the max, (C) raising error for any state version missing, (D) returning max state version in the response?

(Maybe in a follow up PR so that we can merge for Krzysztof)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we're currently missing a tx for state version 0, but this can also be handled; will do in a separate PR

Copy link
Contributor

Choose a reason for hiding this comment

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

Transactions map to resultant state versions, so I think it's right that there is no tranasction mapping to resultant state version 0.

The Genesis transaction would map 0 to 1, so be at resultant state version 1 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup, makes sense, done

@LukasGasior1 LukasGasior1 force-pushed the feature/openapi-rust-preview branch from 0f3afcd to 4a3eb2e Compare August 29, 2022 17:52
Copy link
Contributor

@dhedey dhedey left a comment

Choose a reason for hiding this comment

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

Nice! Let's get this merged. Happy for any fixes to follow

@@ -164,8 +165,10 @@ components:
type: array
items:
type: string
description: The engine return data (only present if status is succeeded)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this true? I imagine if failed, they'd still be output, because there's still state changes and a commit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we might need a better naming :) the state diff is still there, but not the output (as in function return value):

pub enum TransactionOutcome {
    Success(Vec<Vec<u8>>),
    Failure(RuntimeError),
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Ahh right, yes. Sorry, got you. This is the "output", which is really the "transaction executor log" I think? I'm not really sure what it is to be honest xD.

Yulong and I were using the term "Outcome" for Success/Failure :)

@LukasGasior1 LukasGasior1 merged commit e25be39 into main Aug 30, 2022
@LukasGasior1 LukasGasior1 deleted the feature/openapi-rust-preview branch August 30, 2022 11:18
@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

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.

4 participants