Replies: 2 comments 4 replies
|
Thank you for writing this up! One preliminary comment: My original thinking was that keep account components / notes / tx scripts as separate tables rather than putting everything under [package]
name = "miden-standards"
version = "0.1.0"
[lib]
name = "standards"
namespace = "miden::standards"
path = "mod.masm"
[[component]]
name = "basic_wallet"
path = "miden::standards::wallets::basic"
[[component]]
name = "singlesig"
path = "miden::standards::auth::singlesig"
[[note]]
name = "p2id"
path = "miden::standards::notes::p2id"This would output 4 packages:
Also, the above example is pretty "truncated" - for each component/note etc. we'd have additional properties containing component's metadata (e.g., storage layout). What's the motivation for putting every artifact under "component" table? One of the hesitations I have is that it may be confusing to call notes/tx scripts etc. "components" as it is already a pretty overloaded term. |
2 replies
|
Following up on our call, I will:
|
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
A high-level proposal for evolving the Miden package format (
.masp) so a single package can carry multiple account components together with note scripts and transaction scripts in any configuration.Motivation
Primary use case: one package shipping the app's account component(s) plus the note scripts and transaction scripts that interact with them. Secondary: one-file account creation in clients (auth + wallet + policy components described by a single package).
Today one package holds exactly one artifact, and the ecosystem visibly works around it:
ACCOUNT_COMPONENT_METADATAsection, andAccountComponent::from_packagemaps one package to one component. Multi-component accounts require N files (miden-client new-account -p auth/basic-auth -p basic-wallet); auth components ship as separate packages in anauth/subdirectory; the fungible-faucet package cannot carry theTokenPolicyManagercomponent it needs, so the client injects it implicitly.miden-standardsfakes bundling for scripts: all standard note/tx scripts are compiled into one big library package and extracted by path (NoteScript::from_library_reference), while account components ship as separate per-component.maspfiles.miden-agglayerrepeats the same pattern.Dependencydigests and a runtime dependency resolver.Proposed model
Every package is a single MAST forest plus a set of one or more named parts describing what the forest contains. There is no separate "bundle" type; today's single-artifact package becomes the one-part degenerate case. (A package holds parts — the same container-of-parts vocabulary as the Open Packaging Conventions; "component" stays reserved for account components.)
Validation rules:
ExecutableorKernelpart must be the only part in its package. Library-shaped kinds (Library,AccountComponent,Note,TransactionScript) combine freely in any configuration.entrypointis only valid forExecutable/Kernelparts. Note and transaction script parts keep identifying their entry procedure via the existing export attributes (@note_script,@transaction_script), which are unique per part — restoring the uniqueness thatfrom_librarywants and removing the need for path-based extraction workarounds.Package::kind()becomes a derived accessor (executable package iff its single part is executable);TargetTypemoves to the part level.What moves where:
MastForest::mergesemantics — duplicate nodes dedup; advice-map entries are keyed by content digest, so overlaps are identical by construction). References between sibling parts dissolve into the merge and need no dependency records.Dependency.digestis a forest commitment; mapping one to the other requires fetching the dependency's manifest). With a single shared forest the package is the fetch/resolution unit, soPackage::dependencies()(the derived, deduplicated union) drives fetching; the per-part records serve attribution, auditing, and tooling. A givenPackageIdmust resolve to the same version/digest across the whole package (validated).ACCOUNT_COMPONENT_METADATAbecomes part-level — each account component carries its own metadata section, resolving today's one-per-package restriction. Debug sections andPROJECT_SOURCE_PROVENANCEstay package-level: they describe the one forest and the project.Identity, digests, addressing
commitment()— exactly today's package digest semantics; thecontent_digestanalog additionally binds name/version/manifests/semantic sections as today.compute_nodes_commitmentmachinery, per part instead of per package). For account components this matches howAccountCodealready commits to procedure roots.package-id::part-name. Digests remain the cryptographic source of truth.Dependencygains an optional part qualifier:{ name: PackageId, part: Option<PartName>, kind, version, digest }. Resolution always fetches the whole package (digest= the package digest); a part-qualified dependency can additionally bind the part's interface commitment for verification. The qualifier is optional because whole-package dependencies remain the common case (stdlib/protocol/kernel, wholesale library linking) and because part export sets may overlap (a component part selects a subtree of the lib part), so a plain module-path reference does not always attribute to a unique part —Some(part)is for dependencies that mean a specific part, e.g. FPI on an account component.Producer: project assembler and
miden-project.tomlToday the project model allows at most one
[lib]target plus N[[bin]]targets, and the assembler emits one package per selected target. Proposed:miden-project.tomlis the single authoring surface for the whole package, and per-kind tables ([[component]]for account components,[[note]],[[tx-script]]) declare parts in one of two flavors — carved out of the[lib]module tree by module path, or compiled 1:1 from a Rust crate referenced by its folder.An (imaginary) hybrid workspace — the library and one account component written in MASM, everything else in Rust; each crate compiles to exactly one part, and parts are handed to the project assembler in memory (there is no on-disk part format):
[lib](if present) is assembled once, eachcratepart is compiled from its crate, and the project assembler merges all parts' code into the shared forest. Orchestration:cargo miden build→midenc→ project assembler; pure-MASM projects go through themiden bundleCLI. Mixing MASM-carved and crate parts in one project is possible (eventually). Package name/version are stamped from[package](also fixing today's hardcoded0.0.0).deps— sibling parts and external packages alike, the latter resolving to[dependencies]entries (which hold the coordinates: version, linkage). Sibling edges (e.g. a note using its account component) give the assembler the build order and dissolve at package level; external ones are what the built part's manifest records.[[bin]]targets are unchanged: each still produces its own single-partExecutablepackage.[lib]alone, with no part tables, produces today's plain library package.Consumers (high-level deltas)
Protocol (
miden-protocol/miden-standards/miden-tx):AccountComponent::from_package(&pkg, part_name, &init)— the name is optional when the package has exactly one account-component part.AccountComponentCodewraps the shared forest plus the part's manifest and metadata instead of a whole package.NoteScript::from_package(&pkg, part_name)/TransactionScript::from_package(&pkg, part_name)select a script part and use its unique attributed export.from_library_reference-style digest stubs remain for lazy loading.insert_packageand friends) registers the single package forest (and its merged advice map) in theMastForestStore— one insert per package.miden-standardsandmiden-agglayerbuild scripts collapse from "one bundled scripts library + N per-component files" to one multi-part package each — formalizing the bundle pattern they already hand-roll.Client (CLI):
new-account --package dapp.maspenumerates the account-component parts, selects auth via the per-part@auth_scriptattribute, and prompts per part schema; faucet andTokenPolicyManagerfinally ship together. A part filter flag covers partial use.callprocedure lookup gains an optional part qualifier.Compiler (
midenc/cargo-miden) — besides driving production (above), it consumes multi-part packages as dependencies:HybridPackageRegistry→ VM project resolver). A registered package now contributes all of its parts; a project dependency may reference the whole package or a single part, and dependency entries inmiden-project.toml(and cargo metadata) gain the part-qualifiedpackage-id::part-nameform.Dependency; static linking copies the referenced procedures from the dependency's shared forest. The implied link libraries (miden-core,miden-protocol) are unaffected until they themselves become multi-part.#[account(package::Interface)]wrappers become#[account(package::Component)]assuming the component has exactly one interface that has the same name as the component. We probably can enforceComponent name = interface nameeverywhere.Registry / package manager:
PackageIdstays the distribution and version-resolution unit; part-qualified lookups (package-id::part-name) resolve inside the fetched package. Whole-package resolution uses the derived dependency union. Resolver logic is otherwise unchanged.@bitwalker @mooori @bobbinth @PhilippGackstatter @igamigo
All reactions