feat(CLI): Typed rendering for call output#2179
Conversation
|
Hi @igamigo! Quick question about the changelog for this PR: there is already an entry saying “Added miden-cli call command for invoking account procedures directly from the CLI”. Should we reuse that existing entry for this PR as well, or to add a new changelog entry that explicitly mentions typed rendering for the call output? |
I think we can reuse it and add this PR link to the same entry since it's very related, but not a strong opinion so if you prefer otherwise that's fine too. |
|
Can you expand the PR description explaining the design a bit and how it relates to the issue? And/or maybe add some examples of how this renders. I know there is some context there already but the actual implementation and the scope has not been fully discussed AFAICT |
|
Hi @igamigo thanks for your comment! I updated the PR description. There's one thing I just realized, and because of it I'm turning this into a draft: I accidentally tested it against a compiler branch that isn't merged into next yet, this branch is the one that stores the high-level types inside the debug sections. My bad, I didn't check that my |
There was a problem hiding this comment.
Approve direction, but let's hold non-draft on the items below. Thanks @marijamijailovic!
Phase 1 from #2098 is covered.
Before flipping to non-draft though:
- Test coverage. See inline on
package_types/mod.rs—bool,u32,u64are priority types from #2098 but have no unit test. - Compiler dependency. See inline on
tests/cli.rs— the typed path only triggers because the test stubs the debug sections in by hand. - CHANGELOG. The check is still red and I can't drop an inline since
CHANGELOG.mdisn't in the diff. Per your thread with @igamigo, extending the existing #1943 entry sounds right, something like:
Added
miden-cli callcommand for invoking account procedures directly from the CLI (#1943), with typed argument encoding and typed result rendering when the package carries debug type info (#2179).
Follow-ups to track separately, not here:
- Phase 2
SchemaTyperendering. - Module naming:
package_typesreads ambiguous next to the manifest's ownFunctionType. Happy to be overruled.
| proc.encode_args(&["1".to_string(), "2".to_string()]), | ||
| Err(PackageTypesError::TooManyArgs) | ||
| )); | ||
| } |
There was a problem hiding this comment.
Could we add unit tests for bool, u32, and u64 here before non-draft? They're three of the five priority types I called out in #2098 but they're only covered by decode.rs:110-125, not by CI. A mod tests case per type (encode token, decode felts, format_signature) in the same shape as felt_roundtrip would lock the priority-types contract down. I don't think we need a CLI-level test for each, the unit tests should be enough.
There was a problem hiding this comment.
Thanks for comments! I will add the unit tests for bool, u32,u64.
| /// `call` command exercises its typed encode/decode path. Only this proc is annotated; `add` | ||
| /// and `set_value` resolve to `None` in `TypedProcInfo` and stay on the raw path. | ||
| fn build_take_account_id_debug_sections() -> ( | ||
| miden_mast_package::debug_info::DebugFunctionsSection, |
There was a problem hiding this comment.
This helper is load-bearing for the typed test, and it exists because the official compiler does not emit DebugTypeInfo::Function entries yet (per your draft-state note above). That means the only end-to-end coverage of the typed path runs against hand-built debug sections.
Could you link the corresponding compiler PR or open a tracking issue so we have an explicit gate for "pioneers running cargo miden build against the published toolchain will see typed output"? I want to be sure we don't ship this and then quietly have everyone fall back to raw rendering until the compiler catches up.
| pub(super) fn wit_short_name(name: &str) -> &str { | ||
| name.rsplit('/').next().filter(|s| !s.is_empty()).unwrap_or("") | ||
| } | ||
|
|
||
| pub(super) fn is_account_id_type(name: &str) -> bool { | ||
| wit_short_name(name) == "account-id" | ||
| } |
There was a problem hiding this comment.
This ties AccountId detection to WIT-style /-separated names. It works for both the Walnut fork's miden:base/core-types@1.0.0/account-id and a bare account-id, but it assumes any future compiler picks one of those shapes. Worth a comment here pointing at the assumption, or a follow-up so we have one place to update once the upstream compiler picks its final naming for these structs.
There was a problem hiding this comment.
Added a comment for this
| pub mod keystore; | ||
| pub mod note; | ||
| pub mod note_transport; | ||
| pub mod package_types; |
There was a problem hiding this comment.
Not a blocker: package_types reads ambiguous next to the manifest's own FunctionType. Would package_debug_info or package_typed_view describe what the module actually wraps better? Happy to be overruled.
There was a problem hiding this comment.
Agree! I will rename to package_debug_info
a74dc47 to
927cc8f
Compare
|
I’ve pushed a follow-up that addresses the review comments on the typed
|
|
cc @BrianSeong99 I am tagging you here so you are aware of this one |
|
Hi @igamigo! Would it make sense to start the review while keeping this PR in draft until the compiler changes land, and then make this one “ready” once the compiler is ready as well? |
igamigo
left a comment
There was a problem hiding this comment.
Overall I'm not entirely sure the new package_debug_info should be part of miden_client. It seems like it encompasses a bunch of package/compiler-related helpers and is not really used in the miden-client crate, only the CLI. In this sense, I wonder if it should be placed either in the CLI directly or as a separate compiler crate, as it's very much independent of the codebase here.
|
|
||
| pub struct TypedProcInfo { | ||
| types: DebugTypesSection, | ||
| name: String, | ||
| return_type_idx: Option<DebugTypeIdx>, | ||
| params: Vec<TypedParam>, | ||
| } | ||
|
|
||
| #[derive(Clone)] | ||
| struct TypedParam { | ||
| name: String, | ||
| type_idx: DebugTypeIdx, | ||
| } |
There was a problem hiding this comment.
nit: Let's add doc comments to these structs
| pub mod keystore; | ||
| pub mod note; | ||
| pub mod note_transport; | ||
| pub mod package_debug_info; |
There was a problem hiding this comment.
Should this be named just package?
| pub use self::encode::parse_felt_token; | ||
| use self::encode::{arg_token_count, encode_tokens}; | ||
| pub use self::errors::PackageDebugInfoError; |
There was a problem hiding this comment.
nit: Let's move the pub use to be below the mod definitions (ie, below line 4 and 5). Let's also move all the imports to be above the mod definitions
|
|
||
| impl TypedProcInfo { | ||
| /// `None` if the package has no debug info or no entry for `procedure_name`. | ||
| pub fn resolve(package: &Package, procedure_name: &str) -> Option<Self> { |
There was a problem hiding this comment.
nit (feel free to disregard): Should this be named from_package?
| Some(Self { types, name, return_type_idx, params }) | ||
| } | ||
|
|
||
| /// `get-count() -> felt`. |
There was a problem hiding this comment.
Can we expand on these doc comments?
| pub fn format_signature(&self) -> String { | ||
| let params = self | ||
| .params | ||
| .iter() | ||
| .map(|p| format!("{}: {}", p.name, format_type(&self.types, p.type_idx, 0))) | ||
| .collect::<Vec<_>>() | ||
| .join(", "); | ||
| let ret = self | ||
| .return_type_idx | ||
| .map(|i| format!(" -> {}", format_type(&self.types, i, 0))) | ||
| .unwrap_or_default(); | ||
| format!("{}({}){}", self.name, params, ret) | ||
| } |
There was a problem hiding this comment.
Would it be better to make this into a Display impl?
| /// Encodes `tokens` as a flat felt vector matching the procedure's parameter types. `word` | ||
| /// and `account-id` each parse from a single hex token; other structs expect one token per | ||
| /// leaf field. | ||
| pub fn encode_args(&self, tokens: &[String]) -> Result<Vec<Felt>, PackageDebugInfoError> { |
There was a problem hiding this comment.
Do we need this function here? It seems like this is an application responsibility. For example, perhaps a CLI would be interested in taking strings and parsing them, but I don't think we need to make it a concern of TypedProcInfo. Perhaps it can take a list of felts and validate them against the proc info
|
|
||
| /// Number of felts the procedure pushes on the stack for its return value. `Some(0)` if | ||
| /// there is no return type; `None` if the return type has no statically-known felt size. | ||
| pub fn return_value_felt_count(&self) -> Option<usize> { |
There was a problem hiding this comment.
nit: Would output_felt_count be a fitting name for this?
Thanks, that’s a good point. I agree this probably live in I'm thinking about moving the whole typed encoder/decoder into This way the split would be: the compiler writes the debug sections, Let me know what do you think? And we can tag in people from the miden-vm / compiler side who can share their opinion. |
|
Hey @marijamijailovic / @Keinberger, just a heads up, if we want this on the |
|
This PR is blocked for now.
After the fix is introduce, I will:
So this PR stays in draft until then. |
Drop the local package_debug_info module and consume the typed encoder/decoder from miden-mast-package's debug_info::typed instead.
5c1cb85 to
69c7a49
Compare
|
Hey @igamigo, rebased on main, miden-vm PR is open — 0xMiden/miden-vm#3276 |
The above issue is resolved. |
|
With the issue resolved are we now ready to move this PR out of draft and have it reviewed? Or were we waiting on the VM patch release? |
Thanks for bumping this up! Now that the |
This PR extends the call command: it now looks for type info in the package's debug sections, and if it's there it prints a typed signature and decodes the result (for now account-id, bool, word, structs) instead of showing raw felts. If there's no type info it just prints the raw stack like before, so nothing changes for existing packages.
It reads two sections from the package - DEBUG_FUNCTIONS (the procedure name, its params and signature) and DEBUG_TYPES (the type definitions). With those, the args you pass on the CLI get encoded according to the parameter types (e.g. an account-id hex token expands to two felts, a word to four), and the returned felts get decoded back into the same types for printing.
Example, you'll get something like:
Closes #2098