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

Cross-Context Procedure Invocation #303

Open
2 of 11 tasks
bitwalker opened this issue Sep 5, 2024 · 0 comments
Open
2 of 11 tasks

Cross-Context Procedure Invocation #303

bitwalker opened this issue Sep 5, 2024 · 0 comments
Labels
blocker This issue is one of our top priorities feature New feature or request
Milestone

Comments

@bitwalker
Copy link
Contributor

bitwalker commented Sep 5, 2024

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":

  1. 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.
  2. 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.
  3. 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:

Tasks

  1. greenhat
  2. frontend wasm
    greenhat
  3. 0 of 2
    frontend wasm
    greenhat
@bitwalker bitwalker added feature New feature or request blocker This issue is one of our top priorities labels Sep 5, 2024
@bitwalker bitwalker added this to the Beta 2 milestone Sep 5, 2024
@greenhat greenhat modified the milestones: Beta 2, Beta 1 Sep 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocker This issue is one of our top priorities feature New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants