Commit graph

7 commits

Author SHA1 Message Date
a93a2b7cfa phase 3.2: cip-25 metadata via the pallas fork
unblocks named mints. wallet.mint now accepts an optional `metadata`
arg (json object); explorers + wallets render the asset with name/image
instead of <asset1xyz...>.

new aldabra-core::metadata module:
- json_to_metadatum: serde_json::Value → Metadatum (recursive). numbers
  must fit i64 (cardano metadata Int width). strings >64 bytes split
  into Array<Text> chunks at utf-8 char boundaries (CIP-25 v2
  long-string convention). null is rejected.
- build_cip25_aux_data(policy_id_hex, asset_name_hex, json_value):
  builds the label-721 wrapper (Map { 721: Map { policy_bytes:
  Map { name_bytes: attrs }, "version": "2.0" } }), wraps in
  AuxiliaryData::PostAlonzo, returns cbor bytes.

mint module:
- new build_signed_mint_with_metadata + build_unsigned_mint now take
  optional cip25_metadata. backward-compat: build_signed_mint is a
  thin no-metadata wrapper.
- prepare_mint + build_mint_staging plumb aux_data_cbor through.
  staging.auxiliary_data(bytes) is the new fork API surface — when
  set, conway::build_conway_raw decodes + computes
  auxiliary_data_hash automatically.
- regression test build_signed_mint_with_metadata_produces_aux_hash:
  decodes the resulting signed cbor, asserts both
  body.auxiliary_data_hash is Some and tx.auxiliary_data is present.
  catches the failure mode where metadata is silently dropped.

mcp wallet.mint gains a `metadata` arg field surfaced via schemars
JsonSchema. tools/list shape correctly carries the optional json
object.

depends on Sulkta-Coop/pallas@feat-aux-data — vendored via
[patch.crates-io] in the workspace Cargo.toml. PR upstream pending.

56 → 65 unit tests. 8 → 8 mcp tools (count unchanged, wallet.mint
gained an arg).
2026-05-04 12:11:11 -07:00
2f3d975c0f phase 3.1, 3.4, 3.5: native policy + mint path (no metadata yet)
new aldabra-core::mint module:
- PolicySpec enum: SingleSig, SingleSigTimelock, NofK
  - SingleSig{pkh}: ScriptPubkey native script
  - SingleSigTimelock{pkh, slot}: ScriptAll[ScriptPubkey, InvalidHereafter(slot)]
  - NofK{n, [pkhs]}: ScriptNOfK
- PolicySpec::single_sig(payment) + single_sig_timelock(payment, slot)
  convenience constructors that derive the pkh from a PaymentKey.
- policy_id() = pallas_traverse::ComputeHash<28>::compute_hash, which
  is blake2b-224 of (0x00 || cbor) — the canonical native-script hash.
- to_cbor() for callers that want the script bytes raw.

build_signed_mint / build_unsigned_mint:
- two-pass fee like the send path, plus a few extras specific to mint:
  staging.mint_asset(policy, name, qty), .script(Native, cbor),
  .disclosed_signer(payment_pkh) — the disclosed_signer surfaces the
  required signature in the tx body so the chain knows which witness
  to verify against the script.
- positive qty mints (asset goes into dest output), negative qty burns
  (asset comes out of input holdings, change preserves leftover).
- token-bearing change must hold ≥ min_utxo lovelace — same guard as
  the send path.

mcp tools:
- wallet.policy.create — args: invalid_after_slot? — returns
  {policy_id_hex, script_cbor_hex, type}.
- wallet.mint — args: dest_address, dest_lovelace (≥ 1 ADA),
  asset_name_hex, quantity (i64), invalid_after_slot? — auto-generates
  a single-sig policy bound to the wallet's payment key, builds, signs,
  submits.

8 → 10 mcp tools. 48 → 56 unit tests.

3.2 (CIP-25 metadata) is BLOCKED on pallas-txbuilder 0.32/0.35 — both
hardcode `auxiliary_data: None` in the conway builder. options for next
session: (a) post-build CBOR injection, (b) assemble tx via
pallas-primitives directly, (c) wait for upstream. flagged in the
spec doc.

3.3 (CIP-68) depends on 3.2. 3.6 (MAP 2-of-2) needs the multi-key
signing flow on the build side; PolicySpec::NofK variant is ready but
build_signed_mint only sign with one key today.
2026-05-04 11:44:16 -07:00
46b6f6efa3 phase 2.5-2.6: native asset send + cold-sign flow
InputUtxo gains an `assets: BTreeMap<String, u64>` field matching
aldabra-chain::Utxo's shape (`policy_id_hex(56) || asset_name_hex`
key). new AssetSpec type for the recipient asset list.

asset-aware select_utxos:
- phase 1: per-asset greedy by holding size, pulls UTXOs containing
  each requested asset until coverage ≥ target
- phase 2: ada-only greedy to top up lovelace need
this preserves the prior ada-only behavior when assets list is empty.

build_signed_payment_with_assets / build_unsigned_payment_with_assets
build outputs with .add_asset() for each requested + each leftover
(change-side). guards: token-bearing change must hold ≥ min_utxo
ADA — surfaced as a clearer error than letting the chain reject a
sub-min output.

cold-sign flow (phase 2.6):
- new tools wallet.send.unsigned (returns {cbor_hex, summary} json
  for human review + cold-signer consumption) and
  wallet.submit_signed_tx (takes hex-encoded signed cbor → submit).
- PaymentSummary now carries send_assets + change_assets vecs so the
  human reviewer can spot accidental token transfers.
- summary.tx_hash is the predicted body hash; signed CBOR will hash
  to the same value (signature is over the body, not the cbor wrapper).

helpers: hex_encode/decode, parse_policy_id, parse_asset_name,
split_asset_key. mcp side defines its own McpAssetSpec with
schemars::JsonSchema derive so the schemars dep doesn't bleed into
the security-boundary core crate.

48 unit tests (was 41). new coverage: asset-aware selection (greedy +
missing-asset error), policy/asset-name parsers, multi-asset cbor
build, change-asset summary correctness.

phase 2.7 (live preprod smoke against funded wallet) procedure
documented in memory/spec-aldabra-buildout.md; needs cobb's faucet ada.
2026-05-04 11:35:06 -07:00
dd84303885 phase 2.1-2.4: send path — submit + status, txbuilder, wallet.send, wallet.tx_status
chain backend grew submit_tx (POST /submittx, raw cbor body) and
tx_status (POST /tx_info → Confirmed{block,epoch}|NotFound). serde
tag-based status enum so the mcp tool returns clean json.

new core::tx module: ProtocolParams + InputUtxo + build_signed_payment.
two-pass fee refinement — build unsigned, measure size, add witness
overhead constant (128 bytes for vkey+sig+cbor framing), recompute
real fee, build with final fee, sign once (PrivateKey doesn't impl
Clone in pallas-wallet, so we don't double-sign). change below
min-utxo merges into fee instead of emitting dust.

added pallas-txbuilder + pallas-wallet 0.32 deps. PaymentKey gains
crate-private xprv() accessor; payment_key_to_private converts
ed25519-bip32 XPrv → pallas-wallet PrivateKey::Extended via the
64-byte extended secret bytes.

mcp tools.rs: 4 → 6 tools.
- wallet.send (to_address, lovelace, force) with hard-cap guard
- wallet.tx_status (tx_hash) → status json
SendArgs/TxStatusArgs use schemars derive so rmcp generates proper
input schemas. config.rs adds max_send_lovelace (default 100 ADA,
ALDABRA_MAX_SEND_LOVELACE env override).

37 unit tests. mcp tools/list smoke confirms all 6 tools register
with correct schemas (force defaults false, lovelace required uint64,
to_address required string).

phase 2.5 (native-asset send), 2.6 (cold-sign offline mode), and
2.7 (real preprod smoke against a funded wallet) still open.
2026-05-04 11:18:33 -07:00
bc39148b63 phase 1: full read path — bip39 + cip-3 + cip-1852 + koios + age-mnemonic + rmcp
end-to-end working wallet: paste 24-word mnemonic, age-encrypt at rest,
on unlock derive root + payment + stake keys, build cip-19 base address,
serve four tools over mcp stdio (wallet.address, wallet.network,
wallet.balance, wallet.utxos).

deps added: ed25519-bip32 0.4 (pallas only ships raw ed25519, not the
cardano variant of bip32 hd derivation), cryptoxide 0.4 for pbkdf2-hmac-sha512,
age 0.10 for at-rest mnemonic encryption, rpassword 7 for tty-only passphrase
prompts, toml 0.9 for config.toml.

new modules:
- crates/aldabra-core/src/derive.rs — payment + stake key derivation, hash
- crates/aldabra-chain/src/koios.rs — real reqwest impl, asset aggregation
- crates/aldabra-mcp/src/{bootstrap,config,tools}.rs

caught one bug pre-flight: get_balance was clobbering same-asset
quantities across utxos instead of summing. fixed + regression test.

headless support via ALDABRA_PASSPHRASE env (mcp clients own stdin so
the rpassword prompt path can't run). docker secret / systemd
EnvironmentFile sources it in production.

dockerfile: multi-stage rust:1.95-bookworm → debian:bookworm-slim, tini
as pid1, non-root aldabra user, /var/lib/aldabra owned 700.

29 unit tests + 1 ignored live-koios test. preprod smoke test exercised
initialize → tools/list → tools/call wallet.address end-to-end via
piped json-rpc; correct preprod address came back from canonical
abandon-art mnemonic.

phase 2 (send) is next.
2026-05-04 11:09:00 -07:00
1f1993ed97 rename: sulkta-wallet → aldabra (per Cobb 2026-05-04)
Aldabra giant tortoise (Aldabrachelys gigantea) — endemic to the
Aldabra atoll, up to 250 kg, 150-year lifespan. Long-lived,
defended, slow but unstoppable. Better metaphor for the wallet
than 'sulkta-wallet' which was on-the-tin descriptive.

All renames in one pass:
- repo: Sulkta-Coop/sulkta-wallet → Sulkta-Coop/aldabra (via gitea API)
- workspace dir: sulkta-wallet → aldabra
- crate dirs: wallet-{core,chain,mcp} → aldabra-{core,chain,mcp}
- crate names + path imports in Cargo.toml workspace + each crate
- binary name: sulkta-wallet → aldabra
- README, ROADMAP, docs/architecture: all references swept
2026-05-04 10:11:23 -07:00
489b58cc1e phase 1 scaffold: cargo workspace + 3 crates + roadmap + architecture
Repo skeleton for sulkta-wallet, the rust-native cardano lite wallet
with MCP server interface. Builds end-to-end, types in place,
real cardano primitives land next pass.

Crates:
  wallet-core   — pure crypto + types. mnemonic, key derivation,
                  signing. No I/O. Security boundary.
  wallet-chain  — pluggable backends. ChainBackend trait, Koios
                  client (stub for now). Ogmios + submit in phase 2.
  wallet-mcp    — the binary. stdio MCP transport via rmcp.

Phase plan in ROADMAP.md, threat model in docs/architecture.md.

This is also Cobb's first Rust project + a real-world workout for
crafting-table's rust toolchain.
2026-05-04 10:02:32 -07:00