-
Notifications
You must be signed in to change notification settings - Fork 809
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
[FRAME Core] Add support for view_functions
#216
Comments
Would this be implemented on all runtimes per default, or feature gated to keep them small? These functions are run in a new WASM instance, so even if someone changed storage, it would be disregarded, or?
How does this work with multiple inputs? Just some tuple iterator, huh? I think we should directly think about versioning them, maybe similar to the runtime APIs. That will make it easier to communicate breaking changes in their arg/return types. |
Multiple arguments are just a tuple. That is the same way it works for the the runtime api function as well. Basically the extern functions are like runtime api functions.
On all. Maybe we could make it disablable per pallet in
All runtime api functions work this way. We don't need to panic or anything. There are also valid points of temp modifications. The node also should not see any of that and the node doesn't need to know. You can use the
If they are part of the metadata, it should be enough. As a developer you can also do some kind of manual versioning, by just creating a new function and keep the old one. |
This is definitely good for replacing all those getter RuntimeAPIs which are mostly boilerplate. However, it looks to me as a more comfortable way to define a subset of RuntimeAPIs. I think we should take it a step further. The design as described won't allow a contract to use those queries because they can only be called from outside the runtime and not from inside. So we probably want to design it more like a It is basically just the read-only version of |
I like the idea! Sounds like a good solution to solve the issue for contracts and ui in one go! |
view_functions
view_functions
One downside I see with using tuples for this as @athei explained is that we would have to deal with a similar set of issues related preventing breaking changes. The index of the pallet and function will matter, similar to calls. Runtime-apis, which are addressable via their name and are thus independent of the runtime pallet ordering had this extra advantage. I always assumed view functions need to have the same property. Not sure if it is a big issue, just something to be aware of. |
Good point. Maybe we should incorporate more robustness and versioning. If it works well we can backport it to |
We could use a "hash based" index. |
@bkchr given the fact that we want to push the use of XCM/XCQ to cal into the runtime, do you think this issue is still relevant? |
|
I had a brief discussion with @gavofyork about this in Malmö. I noted that |
Yes, but XCM as a standard will probably never abstract over all available functionality of every available chain. And as a standard it will also never be able to evolve that fast as chains itself. |
I think this is exactly the point of XCM. To be generic enough that functionality can be added without amending the standard. At least eventually. That said, it might still be worthwhile to have view functions. However, they would not be useable from contracts because we need a stable target. And this is exactly what XCM is for. |
I am doubtful if this is still needed as a priority feature. It can remain in our backlog and someday tackled, but not immediately. @juangirini what would be great if you and/or someone else already has a good grasp of this and make it a mentor-able issue? or is it too big for that? |
please, makes this a top priority issue |
I haven't had a proper look at this yet tbh, so I couldn't have an estimate in size yet. But big does not necessarily mean that can't be done by contributors outside of Parity.
@xlc Maybe someone from your team is interested in collaborating to this? |
We don’t have any extra capacity. |
hashing sounds like a good idea, we just need to make sure that we take into account instantiable pallet, as there could be more than 1 occurrence of a pallet in construct_runtime.
The outer enum pallet sounds like a good idea but unlike dispatchable call we not only have to encode a call but also decode a generic Response type. Is using pub trait Query {
fn query(&self) -> Vec<u8>
}
Can't we simply call the view function like any other fns. let balance = Self::account_balance(id); From the perspective of an ink! contract, I imagine we would need to wrap the let getter = Balances::Getter::account_balance{ account: id }.into();
let balance: T::Balance = self.env().query(getter).decode()?; |
We probably also want to have this typed. For contracts this isn't that useful, but for UIs etc to get the type. |
I mean the types are defined by the view functions inside the I imagine that metadata and type annotations will derive from this macro as well |
Ahh yeah makes sense! So we have pallet level |
Yes, from what Alex wrote above, I imagine that the macro would implement the Query trait for the Pallet and Runtime, just like we have for RuntimeCall today |
This issue has been mentioned on Polkadot Forum. There might be relevant details there: |
I have been investigating this issue, at the same time Bryan @xlc has begun designing and developing an There is some discussion above about whether
The design proposes to solve this issue with the extension model and I am at a point where I have to decide where to direct my efforts:
At the moment I am leaning towards |
I think I think maybe you can implement 1. in a way that it is agnostic to the payload (Query vs. XCM query) except for the runtime. |
XCQ will cover all use cases of view functions.
The XCQ will provide host calls to invoke extensions and one potential implementation will be calling the host call with such Query data structure so basically we can make XCQ a wrapper of view functions. However, that’s just one of the potential implementation. There could be other ways and each alternatives will have different trade offs. That’s something I need to figure out by analyzing the pros and cons of each potential solution and decide the best suitable one. So I don’t really know if we should continue do the view function as it is. It is possible the work can be directly integrated into XCQ but it is also possible the work will be obsoleted by XCQ. We need to spend more time defining XCQ spec before we can answer this question. |
This is understood. Just trying to find out if view_functions can be a stepping stone on the way to XCQ. Or if we should go for XCQ directly. |
Closes #216. This PR allows pallets to define a `view_functions` impl like so: ```rust #[pallet::view_functions] impl<T: Config> Pallet<T> where T::AccountId: From<SomeType1> + SomeAssociation1, { /// Query value no args. pub fn get_value() -> Option<u32> { SomeValue::<T>::get() } /// Query value with args. pub fn get_value_with_arg(key: u32) -> Option<u32> { SomeMap::<T>::get(key) } } ``` ### `QueryId` Each view function is uniquely identified by a `QueryId`, which for this implementation is generated by: ```twox_128(pallet_name) ++ twox_128("fn_name(fnarg_types) -> return_ty")``` The prefix `twox_128(pallet_name)` is the same as the storage prefix for pallets and take into account multiple instances of the same pallet. The suffix is generated from the fn type signature so is guaranteed to be unique for that pallet impl. For one of the view fns in the example above it would be `twox_128("get_value_with_arg(u32) -> Option<u32>")`. It is a known limitation that only the type names themselves are taken into account: in the case of type aliases the signature may have the same underlying types but a different id; for generics the concrete types may be different but the signatures will remain the same. The existing Runtime `Call` dispatchables are addressed by their concatenated indices `pallet_index ++ call_index`, and the dispatching is handled by the SCALE decoding of the `RuntimeCallEnum::PalletVariant(PalletCallEnum::dispatchable_variant(payload))`. For `view_functions` the runtime/pallet generated enum structure is replaced by implementing the `DispatchQuery` trait on the outer (runtime) scope, dispatching to a pallet based on the id prefix, and the inner (pallet) scope dispatching to the specific function based on the id suffix. Future implementations could also modify/extend this scheme and routing to pallet agnostic queries. ### Executing externally These view functions can be executed externally via the system runtime api: ```rust pub trait ViewFunctionsApi<QueryId, Query, QueryResult, Error> where QueryId: codec::Codec, Query: codec::Codec, QueryResult: codec::Codec, Error: codec::Codec, { /// Execute a view function query. fn execute_query(query_id: QueryId, query: Query) -> Result<QueryResult, Error>; } ``` ### `XCQ` Currently there is work going on by @xlc to implement [`XCQ`](https://github.com/open-web3-stack/XCQ/) which may eventually supersede this work. It may be that we still need the fixed function local query dispatching in addition to XCQ, in the same way that we have chain specific runtime dispatchables and XCM. I have kept this in mind and the high level query API is agnostic to the underlying query dispatch and execution. I am just providing the implementation for the `view_function` definition. ### Metadata Currently I am utilizing the `custom` section of the frame metadata, to avoid modifying the official metadata format until this is standardized. ### vs `runtime_api` There are similarities with `runtime_apis`, some differences being: - queries can be defined directly on pallets, so no need for boilerplate declarations and implementations - no versioning, the `QueryId` will change if the signature changes. - possibility for queries to be executed from smart contracts (see below) ### Calling from contracts Future work would be to add `weight` annotations to the view function queries, and a host function to `pallet_contracts` to allow executing these queries from contracts. ### TODO - [x] Consistent naming (view functions pallet impl, queries, high level api?) - [ ] End to end tests via `runtime_api` - [ ] UI tests - [x] Mertadata tests - [ ] Docs --------- Co-authored-by: kianenigma <[email protected]> Co-authored-by: James Wilson <[email protected]> Co-authored-by: Giuseppe Re <[email protected]> Co-authored-by: Guillaume Thiolliere <[email protected]>
It is basically the idea from @xlc: https://forum.polkadot.network/t/wasm-view-functions/1045
I think we should start low and only add support for these view functions. For this we should add some new attribute to the FRAME macros
pallet::view_function
. It would look similar to this:This code will then generate some declarative macro
implement_view_function
:This
implement_view_function
macro is then called byconstruct_runtime!
to generate these functions. As we prefix each view function with the name of the pallet in the runtime there will be no conflicts for different pallet instances.This only solves the runtime side. The node side can be implemented later, but requires more thinking.
The text was updated successfully, but these errors were encountered: