program coin

// ======================================================
// ZK-Native Coin โ€” TSP-1 (PLUMB)
// Ops: pay (0), lock (1), update (2), mint (3), burn (4)
//
// State: Merkle tree of account leaves + config commitment
// Leaf   = hash(id, bal, nonce, auth, lock,
//               controller, locked_by, lock_data, 0, 0)
// Config = hash(5 authorities + 5 hooks)
// Meta   = hash(name, ticker, teaser, site, custom,
//               price_oracle, volume_oracle, 0, 0, 0)
//
// Authority semantics:
//   Account ops (pay/lock/burn): 0 = self, else dual
//   Config ops (mint/update):    0 = off, else auth
//
// Leaves are Merkle-authenticated against the state root.
// Hook program IDs are output for external proof composition.
// ======================================================
use std.crypto.merkle
use os.neptune.standards.plumb

// --- Leaf hashing (10 fields: 5 core + 3 program control + 2 reserved) ---
fn hash_leaf(
    id: Field,
    bal: Field,
    nonce: Field,
    auth: Field,
    lock: Field,
    controller: Field,
    locked_by: Field,
    lock_data: Field
) -> Digest {
    hash(id, bal, nonce, auth, lock, controller, locked_by, lock_data, 0, 0)
}

// --- Metadata hashing (standalone, descriptive + oracle references) ---
fn hash_metadata(
    name_hash: Field,
    ticker_hash: Field,
    teaser_hash: Field,
    site_hash: Field,
    custom_hash: Field,
    price_oracle: Field,
    volume_oracle: Field
) -> Digest {
    hash(
        name_hash,
        ticker_hash,
        teaser_hash,
        site_hash,
        custom_hash,
        price_oracle,
        volume_oracle,
        0,
        0,
        0
    )
}



// --- Events ---
event Nullifier {
    account_id: Field,
    nonce: Field,
}

event SupplyCheck {
    supply: Field,
}

event SupplyChange {
    old_supply: Field,
    new_supply: Field,
}



// ============================================================
// Op 0: PAY โ€” transfer tokens between accounts
// ============================================================
fn pay() {
    let old_root: Digest = pub_read5()
    let new_root: Digest = pub_read5()
    let supply: Field = pub_read()
    let current_time: Field = pub_read()
    let amount: Field = pub_read()
    let config: Digest = pub_read5()
    // --- Verify config ---
    let cfg_admin: Field = divine()
    let cfg_pay_auth: Field = divine()
    let cfg_lock_auth: Field = divine()
    let cfg_mint_auth: Field = divine()
    let cfg_burn_auth: Field = divine()
    let cfg_pay_hook: Field = divine()
    let cfg_lock_hook: Field = divine()
    let cfg_update_hook: Field = divine()
    let cfg_mint_hook: Field = divine()
    let cfg_burn_hook: Field = divine()
    plumb.verify_config(
        cfg_admin,
        cfg_pay_auth,
        cfg_lock_auth,
        cfg_mint_auth,
        cfg_burn_auth,
        cfg_pay_hook,
        cfg_lock_hook,
        cfg_update_hook,
        cfg_mint_hook,
        cfg_burn_hook,
        config
    )
    // --- Sender account ---
    let s_id: Field = divine()
    let s_bal: Field = divine()
    let s_nonce: Field = divine()
    let s_auth: Field = divine()
    let s_lock: Field = divine()
    let s_controller: Field = divine()
    let s_locked_by: Field = divine()
    let s_lock_data: Field = divine()
    // Authenticate sender leaf against old state root
    let s_leaf: Digest = hash_leaf(
        s_id, s_bal, s_nonce, s_auth, s_lock,
        s_controller, s_locked_by, s_lock_data
    )
    let s_idx: U32 = as_u32(divine())
    merkle.verify(s_leaf, old_root, s_idx, plumb.tree_depth())
    // Account-level authorization (always required)
    plumb.verify_auth(s_auth)
    // Config-level pay auth (0 = self only, else dual)
    if cfg_pay_auth == 0 {
    } else {
        plumb.verify_auth(cfg_pay_auth)
    }
    // Controller co-authorization signal
    if s_controller == 0 {
    } else {
        pub_write(s_controller)
    }
    // Locked-by restriction signal
    if s_locked_by == 0 {
    } else {
        pub_write(s_locked_by)
    }
    // Time-lock check: current_time >= lock_until
    let time_diff: Field = sub(current_time, s_lock)
    plumb.assert_non_negative(time_diff)
    // Sufficient balance
    let new_s_bal: Field = sub(s_bal, amount)
    plumb.assert_non_negative(new_s_bal)
    // --- Receiver account ---
    let r_id: Field = divine()
    let r_bal: Field = divine()
    let r_nonce: Field = divine()
    let r_auth: Field = divine()
    let r_lock: Field = divine()
    let r_controller: Field = divine()
    let r_locked_by: Field = divine()
    let r_lock_data: Field = divine()
    // Authenticate receiver leaf against old state root
    let r_leaf: Digest = hash_leaf(
        r_id, r_bal, r_nonce, r_auth, r_lock,
        r_controller, r_locked_by, r_lock_data
    )
    let r_idx: U32 = as_u32(divine())
    merkle.verify(r_leaf, old_root, r_idx, plumb.tree_depth())
    // --- Compute new leaves ---
    let new_s_nonce: Field = s_nonce + 1
    let new_s_leaf: Digest = hash_leaf(
        s_id, new_s_bal, new_s_nonce, s_auth, s_lock,
        s_controller, s_locked_by, s_lock_data
    )
    let new_r_bal: Field = r_bal + amount
    let new_r_leaf: Digest = hash_leaf(
        r_id, new_r_bal, r_nonce, r_auth, r_lock,
        r_controller, r_locked_by, r_lock_data
    )
    // Authenticate new leaves against new state root
    merkle.verify(new_s_leaf, new_root, s_idx, plumb.tree_depth())
    merkle.verify(new_r_leaf, new_root, r_idx, plumb.tree_depth())
    // Hook signal
    plumb.signal_hook(cfg_pay_hook)
    // Nullifier (sealed โ€” verifier sees commitment)
    seal Nullifier { account_id: s_id, nonce: s_nonce }
    // Supply unchanged
    reveal
    SupplyCheck { supply: supply }
}

// ============================================================
// Op 1: LOCK โ€” time-lock tokens
// ============================================================
fn lock() {
    let old_root: Digest = pub_read5()
    let new_root: Digest = pub_read5()
    let supply: Field = pub_read()
    let lock_time: Field = pub_read()
    let config: Digest = pub_read5()
    // --- Verify config ---
    let cfg_admin: Field = divine()
    let cfg_pay_auth: Field = divine()
    let cfg_lock_auth: Field = divine()
    let cfg_mint_auth: Field = divine()
    let cfg_burn_auth: Field = divine()
    let cfg_pay_hook: Field = divine()
    let cfg_lock_hook: Field = divine()
    let cfg_update_hook: Field = divine()
    let cfg_mint_hook: Field = divine()
    let cfg_burn_hook: Field = divine()
    plumb.verify_config(
        cfg_admin,
        cfg_pay_auth,
        cfg_lock_auth,
        cfg_mint_auth,
        cfg_burn_auth,
        cfg_pay_hook,
        cfg_lock_hook,
        cfg_update_hook,
        cfg_mint_hook,
        cfg_burn_hook,
        config
    )
    // --- Account to lock ---
    let a_id: Field = divine()
    let a_bal: Field = divine()
    let a_nonce: Field = divine()
    let a_auth: Field = divine()
    let a_lock: Field = divine()
    let a_controller: Field = divine()
    let a_locked_by: Field = divine()
    let a_lock_data: Field = divine()
    // Authenticate leaf against old state root
    let a_leaf: Digest = hash_leaf(
        a_id, a_bal, a_nonce, a_auth, a_lock,
        a_controller, a_locked_by, a_lock_data
    )
    let a_idx: U32 = as_u32(divine())
    merkle.verify(a_leaf, old_root, a_idx, plumb.tree_depth())
    // Account-level authorization (always required)
    plumb.verify_auth(a_auth)
    // Config-level lock auth (0 = self only, else dual)
    if cfg_lock_auth == 0 {
    } else {
        plumb.verify_auth(cfg_lock_auth)
    }
    // Lock monotonicity: new lock_until >= old lock_until
    let lock_diff: Field = sub(lock_time, a_lock)
    plumb.assert_non_negative(lock_diff)
    // New leaf with updated lock_until and incremented nonce
    let new_a_nonce: Field = a_nonce + 1
    let new_a_leaf: Digest = hash_leaf(
        a_id, a_bal, new_a_nonce, a_auth, lock_time,
        a_controller, a_locked_by, a_lock_data
    )
    // Authenticate new leaf against new state root
    merkle.verify(new_a_leaf, new_root, a_idx, plumb.tree_depth())
    // Hook signal
    plumb.signal_hook(cfg_lock_hook)
    // Supply unchanged
    reveal
    SupplyCheck { supply: supply }
}

// ============================================================
// Op 2: UPDATE โ€” update token config (admin only)
// ============================================================
fn update() {
    let old_root: Digest = pub_read5()
    let new_root: Digest = pub_read5()
    let supply: Field = pub_read()
    let old_config: Digest = pub_read5()
    let new_config: Digest = pub_read5()
    // State invariant: account tree must not change
    assert_digest(old_root, new_root)
    // --- Verify old config ---
    let old_admin: Field = divine()
    let old_pay_auth: Field = divine()
    let old_lock_auth: Field = divine()
    let old_mint_auth: Field = divine()
    let old_burn_auth: Field = divine()
    let old_pay_hook: Field = divine()
    let old_lock_hook: Field = divine()
    let old_update_hook: Field = divine()
    let old_mint_hook: Field = divine()
    let old_burn_hook: Field = divine()
    plumb.verify_config(
        old_admin,
        old_pay_auth,
        old_lock_auth,
        old_mint_auth,
        old_burn_auth,
        old_pay_hook,
        old_lock_hook,
        old_update_hook,
        old_mint_hook,
        old_burn_hook,
        old_config
    )
    // Admin authorization (0 = renounced, verify_auth will fail)
    plumb.verify_auth(old_admin)
    // --- Verify new config ---
    let new_admin: Field = divine()
    let new_pay_auth: Field = divine()
    let new_lock_auth: Field = divine()
    let new_mint_auth: Field = divine()
    let new_burn_auth: Field = divine()
    let new_pay_hook: Field = divine()
    let new_lock_hook: Field = divine()
    let new_update_hook: Field = divine()
    let new_mint_hook: Field = divine()
    let new_burn_hook: Field = divine()
    plumb.verify_config(
        new_admin,
        new_pay_auth,
        new_lock_auth,
        new_mint_auth,
        new_burn_auth,
        new_pay_hook,
        new_lock_hook,
        new_update_hook,
        new_mint_hook,
        new_burn_hook,
        new_config
    )
    // Hook signal
    plumb.signal_hook(old_update_hook)
    // Supply unchanged
    reveal
    SupplyCheck { supply: supply }
}

// ============================================================
// Op 3: MINT โ€” create new tokens
// ============================================================
fn mint() {
    let old_root: Digest = pub_read5()
    let new_root: Digest = pub_read5()
    let old_supply: Field = pub_read()
    let new_supply: Field = pub_read()
    let amount: Field = pub_read()
    let config: Digest = pub_read5()
    // --- Verify config ---
    let cfg_admin: Field = divine()
    let cfg_pay_auth: Field = divine()
    let cfg_lock_auth: Field = divine()
    let cfg_mint_auth: Field = divine()
    let cfg_burn_auth: Field = divine()
    let cfg_pay_hook: Field = divine()
    let cfg_lock_hook: Field = divine()
    let cfg_update_hook: Field = divine()
    let cfg_mint_hook: Field = divine()
    let cfg_burn_hook: Field = divine()
    plumb.verify_config(
        cfg_admin,
        cfg_pay_auth,
        cfg_lock_auth,
        cfg_mint_auth,
        cfg_burn_auth,
        cfg_pay_hook,
        cfg_lock_hook,
        cfg_update_hook,
        cfg_mint_hook,
        cfg_burn_hook,
        config
    )
    // Mint authorization (always required, 0 = minting disabled)
    plumb.verify_auth(cfg_mint_auth)
    // Supply accounting
    let expected_supply: Field = old_supply + amount
    assert_eq(new_supply, expected_supply)
    // --- Recipient account ---
    let r_id: Field = divine()
    let r_bal: Field = divine()
    let r_nonce: Field = divine()
    let r_auth: Field = divine()
    let r_lock: Field = divine()
    let r_controller: Field = divine()
    let r_locked_by: Field = divine()
    let r_lock_data: Field = divine()
    // Authenticate old recipient leaf against old state root
    let r_leaf: Digest = hash_leaf(
        r_id, r_bal, r_nonce, r_auth, r_lock,
        r_controller, r_locked_by, r_lock_data
    )
    let r_idx: U32 = as_u32(divine())
    merkle.verify(r_leaf, old_root, r_idx, plumb.tree_depth())
    // New recipient leaf (balance increased)
    let new_r_bal: Field = r_bal + amount
    let new_r_leaf: Digest = hash_leaf(
        r_id, new_r_bal, r_nonce, r_auth, r_lock,
        r_controller, r_locked_by, r_lock_data
    )
    // Authenticate new leaf against new state root
    merkle.verify(new_r_leaf, new_root, r_idx, plumb.tree_depth())
    // Hook signal
    plumb.signal_hook(cfg_mint_hook)
    // Supply change
    reveal
    SupplyChange { old_supply: old_supply, new_supply: new_supply }
}

// ============================================================
// Op 4: BURN โ€” destroy tokens
// ============================================================
fn burn() {
    let old_root: Digest = pub_read5()
    let new_root: Digest = pub_read5()
    let old_supply: Field = pub_read()
    let new_supply: Field = pub_read()
    let current_time: Field = pub_read()
    let amount: Field = pub_read()
    let config: Digest = pub_read5()
    // --- Verify config ---
    let cfg_admin: Field = divine()
    let cfg_pay_auth: Field = divine()
    let cfg_lock_auth: Field = divine()
    let cfg_mint_auth: Field = divine()
    let cfg_burn_auth: Field = divine()
    let cfg_pay_hook: Field = divine()
    let cfg_lock_hook: Field = divine()
    let cfg_update_hook: Field = divine()
    let cfg_mint_hook: Field = divine()
    let cfg_burn_hook: Field = divine()
    plumb.verify_config(
        cfg_admin,
        cfg_pay_auth,
        cfg_lock_auth,
        cfg_mint_auth,
        cfg_burn_auth,
        cfg_pay_hook,
        cfg_lock_hook,
        cfg_update_hook,
        cfg_mint_hook,
        cfg_burn_hook,
        config
    )
    // --- Account to burn from ---
    let a_id: Field = divine()
    let a_bal: Field = divine()
    let a_nonce: Field = divine()
    let a_auth: Field = divine()
    let a_lock: Field = divine()
    let a_controller: Field = divine()
    let a_locked_by: Field = divine()
    let a_lock_data: Field = divine()
    // Authenticate leaf against old state root
    let a_leaf: Digest = hash_leaf(
        a_id, a_bal, a_nonce, a_auth, a_lock,
        a_controller, a_locked_by, a_lock_data
    )
    let a_idx: U32 = as_u32(divine())
    merkle.verify(a_leaf, old_root, a_idx, plumb.tree_depth())
    // Account-level authorization (always required)
    plumb.verify_auth(a_auth)
    // Config-level burn auth (0 = self only, else dual)
    if cfg_burn_auth == 0 {
    } else {
        plumb.verify_auth(cfg_burn_auth)
    }
    // Controller co-authorization signal
    if a_controller == 0 {
    } else {
        pub_write(a_controller)
    }
    // Locked-by restriction signal
    if a_locked_by == 0 {
    } else {
        pub_write(a_locked_by)
    }
    // Time-lock check
    let time_diff: Field = sub(current_time, a_lock)
    plumb.assert_non_negative(time_diff)
    // Sufficient balance
    let new_a_bal: Field = sub(a_bal, amount)
    plumb.assert_non_negative(new_a_bal)
    // Supply accounting
    let expected_supply: Field = sub(old_supply, amount)
    assert_eq(new_supply, expected_supply)
    // New leaf
    let new_a_nonce: Field = a_nonce + 1
    let new_a_leaf: Digest = hash_leaf(
        a_id, new_a_bal, new_a_nonce, a_auth, a_lock,
        a_controller, a_locked_by, a_lock_data
    )
    // Authenticate new leaf against new state root
    merkle.verify(new_a_leaf, new_root, a_idx, plumb.tree_depth())
    // Hook signal
    plumb.signal_hook(cfg_burn_hook)
    // Nullifier (sealed โ€” verifier sees commitment)
    seal Nullifier { account_id: a_id, nonce: a_nonce }
    // Supply change
    reveal
    SupplyChange { old_supply: old_supply, new_supply: new_supply }
}

// ============================================================
// Entry point โ€” PLUMB dispatch by operation code
// ============================================================
fn main() {
    let op: Field = pub_read()
    if op == 0 {
        pay()
    } else if op == 1 {
        lock()
    } else if op == 2 {
        update()
    } else if op == 3 {
        mint()
    } else if op == 4 {
        burn()
    }
}

Local Graph