* 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>
* fix(state_machine): reject public transactions that silently drop a declared account
ValidatedStateDiff::from_public_transaction never checked that the accounts
touched in a program's output matched the caller-declared message.account_ids
— it just folded whatever pairs the program returned into the diff. A program
(or a macro-generated dispatcher wrapping one) that silently drops an account
from both sides of its own output together stays internally consistent
(pre_states.len() == post_states.len()) and passes validate_execution's
existing checks, so the dropped account simply vanishes with no error.
Add a check after the chained-call loop: every account_id in
message.account_ids must appear in the final state_diff, or the transaction is
rejected with the new DeclaredAccountMissingFromOutput error.
Add a dropped_account test-guest program that reproduces the exact shape of
the bug (two pre_states in, one consistent (pre, post) pair out) and a
regression test proving the transaction is now rejected. Verified the test
fails with Ok(()) when the check is removed, and passes once it's restored.
* style: tighten comments and apply nightly rustfmt
Condense the explanatory comments on the new account-accounting check and the
dropped_account test guest, and fix imprecise wording (the account is dropped
from both pre_state and post_states together, not just pre_state). Also
applies cargo +nightly fmt's wrapping of the new error message.
* chore: regenerate test fixture after rebase onto dev
Cross-zone builtin programs are no longer registered in the production
genesis. A zone that participates declares the ones it uses via a new
GenesisAction::DeployProgram (sequencer) and a matching deploy_programs
list (indexer), both resolved through CrossZoneProgram and registered
with with_programs. Cross-zone genesis accounts (inbox config,
wrapped-token config) are seeded through the state constructor for a
receiving zone, and bridge-lock holdings are seeded from their actions
regardless of receiving config, dropping V03State::insert_genesis_account.
GenesisAction amounts now use the Balance alias. Documents cross_zone as
the reference LEZ adapter and the bridge demo as not production-safe.
The sequencer's DeployProgram set and the indexer's deploy_programs are
configured separately, so both nodes now log a deterministic genesis
fingerprint (V03State::genesis_fingerprint) at startup: equal values
confirm the two genesis states agree, a mismatch flags a divergent
deploy set.
BREAKING CHANGE: the genesis state root changes (cross-zone builtins are
out of production genesis) and the sequencer/indexer configs gain the
DeployProgram / deploy_programs list that cross-zone-participating zones
must set.