You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a tracking issue for the implementation of the call instruction, and more importantly, support for cross-context calls in Rust, e.g. invoking account methods from a note script.
Background
Supporting this in Rust, properly, requires us to build on top of the WebAssembly Component Model. This is because fundamentally, Rust does not have any notion of address space isolation, or more generally, a way to enforce shared-nothing boundaries. The Component Model requires cross-component communication to be done using a specific ABI, whose semantics do not permit sharing memory between caller and callee. This is not only dictated by the ABI, but is actually enforced by the WebAssembly runtime itself, so it is not possible to smuggle pointers across the boundary and access memory that way.
All of this is represented in Rust using three "tricks":
A Rust crate corresponds to a single component, thus all code in that crate (including code referenced from normal Cargo dependencies) is executed within the same "shared-everything" boundary.
Defining a component, requires expressing the interface(s) of that component using WIT (WebAssembly Interface Types). WIT only supports types which are valid in the Component Model ABI. Thus the functions that form the component boundary all, by construction, adhere to the Component Model ABI, enforcing the shared-nothing nature of the boundary.
In order to call into another component (from Rust), you need to add a new kind of dependency (component) to your project. Unlike normal Cargo dependencies, none of the code in the dependency is directly linked against. Instead, bindings are generated from the WIT description of the component, which not only handle the mechanical details of lifting/lowering the Rust ABI to the Component Model ABI, but also declare a link-time dependency on the actual implementation of those functions. At runtime, all components are instantiated and linked together. This prevents accidental inlining of code from another component written in Rust, into your own, and thus potentially breaking the assumptions of the caller and callee.
In addition to guaranteeing the shared-nothing semantics we want (corresponding to the shared-nothing semantics of Miden contexts), this also has the effect of enabling components to be implemented in any language, with consumers of those components not needing to be aware of what language was used to implement it. This is particularly desirable in the context of Miden, where we do not want to bake in Rust-specific details into accounts, note scripts, and more.
Requirements
While we do not intend to support the full breadth of what is possible with the Component Model in the near term, the key requirements for us, in order to implement support for call, are:
The ability to parse Wasm Component Model artifacts
The ability to recognize that a function call crosses a component boundary, which we will use to distinguish normal procedure calls (exec) from cross-context procedure calls (call).
Access to the Component Model ABI type signature, so that we can properly manage the calling convention at call boundaries. For example, if we cannot fit all callee arguments in the top 16 elements of the operand stack, we must arrange to place them in the advice provider, in such a way that they can be retrieved by the callee. The callee, likewise, must have that retrieval code generated in its prologue.
Support for Component Model resource types will not be implemented for the time being, as they rely on runtime support from the host (in our case, the Miden VM), which we cannot easily implement at this time. Similarly, there may be other aspects of the Component Model, that we cannot support without deeper integration with the VM. As of this writing, the only thing other than resources that would require that kind of support, are component instances. Instances bind the code of a component to a persistent context, thus calling code in an instance will always execute in that instance's context. Miden does not (yet) support explicit context switching, instead it is implicit in call, and callees are always executed in a fresh context. This is a pretty fundamental feature of the Component Model, but our assumption is that most cross-component calls in the Miden universe, are not the sort that will rely on instance state being persistent across calls, only global state, such as account storage, or the rollup itself. We will need to pay close attention to this however.
Implementation
The following are the specific tasks which must be completed before we can consider this feature implemented:
The content you are editing has changed. Please copy your edits and refresh the page.
This is a tracking issue for the implementation of the
call
instruction, and more importantly, support for cross-context calls in Rust, e.g. invoking account methods from a note script.Background
Supporting this in Rust, properly, requires us to build on top of the WebAssembly Component Model. This is because fundamentally, Rust does not have any notion of address space isolation, or more generally, a way to enforce shared-nothing boundaries. The Component Model requires cross-component communication to be done using a specific ABI, whose semantics do not permit sharing memory between caller and callee. This is not only dictated by the ABI, but is actually enforced by the WebAssembly runtime itself, so it is not possible to smuggle pointers across the boundary and access memory that way.
All of this is represented in Rust using three "tricks":
In addition to guaranteeing the shared-nothing semantics we want (corresponding to the shared-nothing semantics of Miden contexts), this also has the effect of enabling components to be implemented in any language, with consumers of those components not needing to be aware of what language was used to implement it. This is particularly desirable in the context of Miden, where we do not want to bake in Rust-specific details into accounts, note scripts, and more.
Requirements
While we do not intend to support the full breadth of what is possible with the Component Model in the near term, the key requirements for us, in order to implement support for
call
, are:exec
) from cross-context procedure calls (call
).call
boundaries. For example, if we cannot fit all callee arguments in the top 16 elements of the operand stack, we must arrange to place them in the advice provider, in such a way that they can be retrieved by the callee. The callee, likewise, must have that retrieval code generated in its prologue.Support for Component Model resource types will not be implemented for the time being, as they rely on runtime support from the host (in our case, the Miden VM), which we cannot easily implement at this time. Similarly, there may be other aspects of the Component Model, that we cannot support without deeper integration with the VM. As of this writing, the only thing other than resources that would require that kind of support, are component instances. Instances bind the code of a component to a persistent context, thus calling code in an instance will always execute in that instance's context. Miden does not (yet) support explicit context switching, instead it is implicit in
call
, and callees are always executed in a fresh context. This is a pretty fundamental feature of the Component Model, but our assumption is that most cross-component calls in the Miden universe, are not the sort that will rely on instance state being persistent across calls, only global state, such as account storage, or the rollup itself. We will need to pay close attention to this however.Implementation
The following are the specific tasks which must be completed before we can consider this feature implemented:
Tasks
wasmtime
towit-bindgen
#116cargo-miden
#117The text was updated successfully, but these errors were encountered: