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

Remove sp-api requirement on compile time bounds #27

Open
Tracked by #11743
bkchr opened this issue Feb 7, 2023 · 8 comments
Open
Tracked by #11743

Remove sp-api requirement on compile time bounds #27

bkchr opened this issue Feb 7, 2023 · 8 comments
Assignees
Labels
I4-refactor Code needs refactoring.

Comments

@bkchr
Copy link
Member

bkchr commented Feb 7, 2023

sp-api or better known as runtime api currently requires on the node compile time bounds. This means when you want to use a certain runtime api, you need to ensure that you pass some type around that implements this runtime api. The idea behind this was that people are made aware at compile time which runtime apis will be required. The problem is that with the removal of the native runtime we can not implement the compile time check any more. This compile time check also had quite some disadvantages like introducing high compile times by making everything statically typed and requiring the compiler to resolve all types to ensure that the required api is implemented. As the check was at compile time, it also complicated shipping a node that supports a runtime api while not having the runtime api enabled yet in the runtime.

So, we should remove this requirement on compile time bound checking. While doing this we should also improve the runtime api usage. It was a big mistake to put the at as first parameter automatically to every runtime api function or generating these with_context functions. The proper solution for this is to create some builder pattern like approach for setting up a runtime api instance. This runtime api instance can then be used to call into the runtime for every possible runtime api.

Part of: #62

@rphmeier
Copy link
Contributor

rphmeier commented Jul 22, 2023

I actually think the compile-time generics are quite cool and allow clean expression of which runtime APIs are required in a function and helps name resolution in the compiler. The only issue is that the implementations are generated in impl_runtime_apis, not decl_runtime_apis, so the native runtime becomes a factor.

One implementation approach that I think would be pretty light-touch is this:

  1. We still ask functions to use where T: ProvideRuntimeApi, T::Api: SomeApi syntax.
  2. We alter decl_runtime_apis to produce a blanket implementation, something like
impl<T: GenericRuntimeApi> ThisApi for T {
   // The `GenericRuntimeApi` trait is just a thin wrapper around wasm, that deals with raw function names and `Vec`s
}
  1. We add a blanket implementation to Client in client/service:
impl ProvideRuntimeApi for Client {
    type Api = SomeTypeImplementingGenericRuntimeApi;

    // ...
}
  1. We remove the trait implementations from impl_runtime_apis and just have it generate wasm exports.

This approach would effectively implement all APIs for every client, and the detection would be done at runtime. It would also be fully backwards compatible with the way we've used runtime APIs in higher-level code so far.

@bkchr
Copy link
Member Author

bkchr commented Jul 22, 2023

Thanks for the input, but what you are proposing only brings the advantage of seeing in the docs automatically what kind of runtime api is required. The downside of this is that it adds extra complexity which also slows down compilation. Expressing the required runtime api in docs can also be done manually by the dev. As it would be a blanket implementation, it would any way never appear at compile time that the runtime api is missing.

I have already some working branch where these compile time checks are removed. I also want to add very easy checking for a runtime api at runtime so that at initialization this can be used to bail out early and the inform people on what to do.

@rphmeier
Copy link
Contributor

I think that is fair - it's also to have slightly less "magic" for the users and better readability of the code. I would like to have the API be something fairly explicit, where the person writing the code has to write

MyApi::some_function(client, at, params);

(or anything including the name of the API)
rather than

client.some_function(at, params)

Though this is mostly stylistic, I believe a good API here should require some explicitness for the purpose of making code easier to read.

@koushiro
Copy link
Contributor

koushiro commented Aug 1, 2023

@bkchr I see that you have introduced an new error UsingSameInstanceForDifferentBlocks in paritytech/substrate#14387 .
According to the description of this issue, the runtime api design should be like the following, right?

let api = client.runtime_api(at);
api.some_function(params);

Is anyone currently working on this?

@bkchr
Copy link
Member Author

bkchr commented Aug 1, 2023

Is anyone currently working on this?

Yeah, I'm working on this.

@atenjin
Copy link
Contributor

atenjin commented Aug 1, 2023

yeah @koushiro and I are waiting for this, there is a bug in frontier eth rpc related with this issue. Though we can skip this part and fix that thing directly, I think if this change is finished and pick to polkadot-1.0.0 branch is more better for us and all ecology parters.

@rphmeier
Copy link
Contributor

rphmeier commented Aug 6, 2023

@bkchr - are you doing this in a way which breaks downstream typechecks? As far as I can tell, we do need some additional code in decl_runtime_apis which does a blanket implementation to avoid that.

And I still believe that

let api = client.runtime_api(at);
api.some_function(params);

is a bad API, and it should be more like

let api = client.runtime_api(at);
MyApi::some_function(api, params);

@bkchr
Copy link
Member Author

bkchr commented Aug 17, 2023

let api = client.runtime_api(at);
MyApi::some_function(api, params);

Yeah that should be possible. I can see the advantage of being more verbose on declaring the trait.

yeah @koushiro and I are waiting for this, there is a bug in frontier eth rpc related with this issue. Though we can skip this part and fix that thing directly, I think if this change is finished and pick to polkadot-1.0.0 branch is more better for us and all ecology parters.

In the future it will not be possible to re-use a runtime api instance for calls to different blocks at compile time. Currently I added this runtime check that will throw the error you are mentioning, but you can just create a new instance of the api to work around this.

@the-right-joyce the-right-joyce transferred this issue from paritytech/substrate Aug 24, 2023
@the-right-joyce the-right-joyce added I4-refactor Code needs refactoring. and removed I7-refactor labels Aug 25, 2023
jonathanudd pushed a commit to jonathanudd/polkadot-sdk that referenced this issue Apr 10, 2024
* Add the scripts.

* Add license preamble.

* Change existing license headers.
liuchengxu added a commit to subcoin-project/polkadot-sdk that referenced this issue Sep 20, 2024
* Use API from rust-bitcoin instead of calling bitcoinconsensus directly

* Nits

* Introduce chain_params module

* Check whether utxo is spent in current block

* Introduce const MEDIAN_TIME_SPAN in chain_params

* Use chain_params.csv_height

* Handle max_outbound_peers properly

* Handle max_inbound_peers properly

* Add script_flag_exceptions

* Fix tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
I4-refactor Code needs refactoring.
Projects
Status: backlog
Development

No branches or pull requests

5 participants