Chained-call and public-transaction dispatch now address the target
program directly by AccountId instead of routing through ProgramId and
converting internally, closing the gap that blocked PDA-addressed
program invocation. The field is named program_account_id (not
account_id) to stay unambiguous next to the account_ids list it sits
beside in the same structs.
Execution/PDA-derivation logic that fundamentally needs the RISC0 image
id (self_program_id, caller_program_id, env::verify, PDA seed
derivation) stays ProgramId-typed, recovering it from the dispatched
AccountId via the existing bijection where needed.
Account.program_owner is now AccountId-typed instead of ProgramId,
via a new bijective From<ProgramId> for AccountId / From<AccountId>
for ProgramId conversion pair (pure byte reinterpretation, not a
hash - both types are exactly 32 bytes). Adds DEFAULT_PROGRAM_OWNER
as the AccountId-typed counterpart to DEFAULT_PROGRAM_ID, used at
every program_owner comparison/claim site instead of an inline
AccountId::default().
Touches every call site across lee_core, lee (including the
guest-side privacy-preserving circuit), all 16 deployed guest
programs, wallet/wallet-ffi, indexer_ffi/indexer_service/
indexer_service_protocol, sequencer_core, testnet_initial_state,
system_accounts, cross_zone, storage, cycle_bench, and
integration_tests - mostly mechanical .into() conversions, plus two
simplifications: wallet's manual base58 encode/decode of
program_owner was dead code once it's AccountId (which already has
Display/FromStr), and the FFI crates' program_owner field now reuses
the existing generic FfiBytes32 wrapper instead of the now-unused
FfiProgramId one.
Rebuilds every guest ELF artifact and the prebuilt sequencer test
fixture via just build-artifacts, since execute_and_prove runs
against the checked-in precompiled privacy_preserving_circuit.bin,
which isn't rebuilt automatically by cargo test/check.
BREAKING!
Before: Providing `nsk` was the same as authorizing a regular private
account.
After: A separate `ask` is required, making unauthorized private
updates possible.
BREAKING!
Before: The ciphertext key got generated via using the commmitment and
its index as the key.
After: The ciphertext key replaces the commitment dependence by a
nullifier dependence, still making the key unique by global state. The
nullifier is assumed to be the nullifier of the pre-state of the account.
Mitigation: Use the new decryption algorithm.
* refactor: rename PrivateUnauthorized to PrivateForeignInit
The account_identity's is_authorized flag no longer determines
authorization for this variant, so keep the name tied to what
actually distinguishes it: no nsk, only npk (a foreign account init).
* chore: rebuild guest artifacts and bump spin to clear yanked advisory
Regenerate ELF artifacts after the PrivateForeignInit rename in lee_core
(compiled into every guest program), and update spin 0.9.8 -> 0.9.9 since
0.9.8 was yanked from crates.io, per cargo deny check advisories.
* test: align is_authorized with PrivateForeignInit's flipped semantics
Recipient pre-states built for PrivateForeignInit now need is_authorized:
true to match the assertion in output.rs. Also rewrites the boundary test
that checked the old invalid case to check the new one, and updates
stale "unauthorized" wording left over from the PrivateUnauthorized name.
* chore: rebuild guest artifacts
Reproducible across repeated local builds; likely toolchain drift since
the prior artifact commit rather than a source change, since no
guest-relevant source or Cargo.lock changed in between.
* fix(tests): align integration tests with PrivateForeignInit and regenerate fixture
prove_init_with_commitment_root (private.rs) and build_privacy_transaction
(tps.rs) still built PrivateForeignInit recipients with is_authorized: false,
same stale-semantics bug fixed earlier in the lee crate's own tests.
The prebuilt sequencer DB dump embeds program IDs derived from guest ELF
bytes, which shifted once the PrivateForeignInit rename changed lee_core
(compiled into every guest program). The stale dump caused widespread
"Unknown program" failures across integration test suites that exercise
deployed programs (wallet_ffi, auth_transfer, bridge, amm, token, pinata,
ata, indexer state-consistency checks). Regenerated via
`just regenerate-test-fixture`.
* fix(tests): rename leftover PrivateUnauthorized to PrivateForeignInit and regenerate fixture
* test: align is_authorized with PrivateForeignInit's flipped semantics
* chore: regenerate test fixture after rebase onto dev
* chore: regenerate test fixture after rebase onto dev
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* refactor(keycard_wallet): replace keycard-py (pyo3) with native keycard-rs
Rewrites the Keycard integration to talk to the LEE-flavored applet
directly from Rust via keycard-rs (pinned git dependency), dropping the
pyo3 shim, python_path.rs, and the vendored keycard-py Python library
entirely. Verified end-to-end against real hardware: pairing, PIN
verification, mnemonic loading, BIP340 Schnorr signing, and transfers
between keycard and public/private accounts.
Also cleans up the pyo3 usage that had leaked into wallet's signing
path (signing.rs, account_manager.rs) beyond the keycard CLI itself,
and trims wallet_with_keycard.sh's Python setup down to just pyscard,
which is only needed by the force_unpower.py test helper now.
* Updated to v2 secure communication
* ci: install libpcsclite-dev for pcsc crate builds
* docs(keycard): clarify personalization is mandatory and document default CA
Reinstalling the applet via `./gradlew install` wipes any existing
personalization regardless of firmware source, which wasn't previously
called out and is a common source of "card not available" confusion.
Also state plainly that personalization is required before any command
works, and document keycard-rs's actual baked-in default CA public key
instead of only describing the override mechanism.
Export KEYCARD_CA_PUBLIC_KEY in all keycard test scripts so they work
against the dev/test-CA personalization flow they rely on.
* fix(keycard): address PR #595 review comments
- Propagate the load_mnemonic error instead of swallowing it behind
is_ok(), matching every other command handler in this file.
- Add deny.toml allowing the keycard-rs git source, fixing the
source-not-allowed failure in `cargo deny check`. Licenses and
advisories checks still have pre-existing, separate failures not
addressed here.
- Drop the pinned rev for the keycard-rs git dependency so it tracks
the upstream default branch instead.
* chore(deny): trim deny.toml to essentials and allow all in-use licenses
Replace the full cargo-deny init template with just the two sections
that matter: the keycard-rs git-source allowance, and an explicit
license allow-list covering every license currently in the dependency
tree. cargo deny check now passes on sources, bans, and licenses;
advisories still fails on a pre-existing, unrelated RUSTSEC advisory.
* fix(deny): remove second config and add source to the original one
* chore: pin keycard-rs dependency and fix factory-reset debug-gate doc
* chore: regenerate test fixture and lockfile after rebase onto dev
---------
Co-authored-by: Daniil Polyakov <arjentix@gmail.com>