-
Notifications
You must be signed in to change notification settings - Fork 242
Use firefly-signer library and allow numbers expressed as strings #837
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
Conversation
Signed-off-by: Nicko Guyer <nicko.guyer@kaleido.io>
Signed-off-by: Nicko Guyer <nicko.guyer@kaleido.io>
peterbroadhurst
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nguyer - I paused the review when I hit quite a big question, about whether you'd used the functions introduced in the ABI parsing structure specifically to handle features like OpenAPI generation. Could you take a look and see if there's more to do, either in Core, or in firefly-signer, to ensure identical recursive processing on the metadata generation and runtime?
Signed-off-by: Nicko Guyer <nicko.guyer@kaleido.io>
Codecov Report
@@ Coverage Diff @@
## main #837 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 301 301
Lines 19718 19797 +79
=========================================
+ Hits 19718 19797 +79
Continue to review full report at Codecov.
|
|
Just realized there are some major merge conflicts here, including some changes that actually need to go into |
Signed-off-by: Nicko Guyer <nicko.guyer@kaleido.io>
Signed-off-by: Nicko Guyer <nicko.guyer@kaleido.io>
Signed-off-by: Nicko Guyer <nicko.guyer@kaleido.io>
internal/apiserver/ffi2swagger.go
Outdated
| func (og *ffiSwaggerGen) addMethod(routes []*ffapi.Route, method *core.FFIMethod, hasLocation bool) []*ffapi.Route { | ||
| description := method.Description | ||
| if method.Details != nil && len(method.Details) > 0 { | ||
| description = fmt.Sprintf("%s\n\nAdditional smart contract details:\n\n%s", description, buildDetailsTable(method.Details)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Struggling a little to understand this. What is the end to end of the details? Is it the place where devdocs would be specified?
One specific item (along with the wider question) is the Additional smart contract details string needs to be translated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! I did forget to mention that in the description, sorry about that. The short version is that it's a way to bubble up blockchain specifics about a function, like if it's payable or not.
Good call out on the string translation, thanks.
…t docs. Signed-off-by: Nicko Guyer <nicko.guyer@kaleido.io>
| } | ||
| } | ||
| if payable, ok := method.Details["payable"]; ok { | ||
| abiEntry.Payable = payable.(bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I note this will mean we panic if someone puts {"payable":"true"} into the field. I wonder if it makes sense to use the JSONObject field getters here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good suggestion. I'll change this one and the similar checks for other fields.
| } | ||
| switch typeComponent.ComponentType() { | ||
| case abi.ElementaryComponent: | ||
| t := e.getFFIType(typeComponent.ElementaryType().String()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I note it was intended you could compare these against the list of elementary types here:
https://pkg.go.dev/github.com/hyperledger/firefly-signer/pkg/abi#pkg-variables
(rather than needing to keep a separate list of strings to compare to)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we still need this bit of code, because somewhere we need a mapping from ABI type (e.g. uint256) to the JSON input type we will accept on the API (e.g. integer)
| var intRegex, _ = regexp.Compile("^u?int([0-9]{1,3})$") | ||
| var bytesRegex, _ = regexp.Compile("^bytes([0-9]{1,2})?") | ||
|
|
||
| func (v *FFIParamValidator) Compile(ctx jsonschema.CompilerContext, m map[string]interface{}) (jsonschema.ExtSchema, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to work out where this is used. I think we agreed that the connector would be responsible for verifying input, and that we should ensure that happens synchronously before we give back a 202 to an invoke.
Understand this PR might not be the final step in the chain, but I want to make sure we're not trying to replicate the connector input validation logic in core.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The most important use of this is in the contracts manager
firefly/internal/contracts/manager.go
Lines 112 to 118 in e841984
| func (cm *contractManager) newFFISchemaCompiler() *jsonschema.Compiler { | |
| c := core.NewFFISchemaCompiler() | |
| if cm.ffiParamValidator != nil { | |
| c.RegisterExtension(cm.ffiParamValidator.GetExtensionName(), cm.ffiParamValidator.GetMetaSchema(), cm.ffiParamValidator) | |
| } | |
| return c | |
| } |
Once this JSON Schema extension is registered, the JSON Schema library calls this function when a request is made to broadcast a new FFI. This ensures that the schema specified in the FFI for each field adheres to the specific rules defined by each blockchain connector. As an example, the Ethereum connector requires the details object in most places, and it requires that a type field is set inside there (in addition to a bunch of other schema rules).
In other words, this is the metaschema (the schema for the schema). This code is not for validating blockchain invocation request input.
peterbroadhurst
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of comments for consideration before merge @nguyer - but this looks great!
Signed-off-by: Nicko Guyer <nicko.guyer@kaleido.io>
This PR introduces
firefly-signer's ABI parsing functions and uses them in FireFly's ABI -> FFI generation code. It also replaces any existing ABI data structures that were originally copied over from Ethconnect, with those offirefly-signer.This PR also makes some enhancements to the ABI -> FFI generation code itself to allow numbers to be expressed in a JSON string or a JSON number. This is reflected in the generated OpenAPI Spec for custom contracts, including a description in the Swagger UI that describes this.
Further improvements have also been made to the OpenAPI Spec / Swagger UI for custom smart contracts, to indicate if a function is payable, etc. The
optionsobject also appears in the schema to make it more obvious how to pass in additional options.A new
detailsfield has been added toFFIMethodandFFIEventDefinitionstructs. In the same way that adetailsfield has always wrapped blockchain specific information for aFFIParam, we now have this ability on a function and event level as well. For an Ethereum smart contract, this is used to indicate whether a function is payable, whether an event is anonymous, etc. The details are simply stored as afftypes.JSONObject. In the rendered Swagger UI, a table is constructed to show each field and its corresponding value in the Function / Event description.