program coin_v2
// PLUMBv2 full-Digest authority and atomic transition example.
// Normative schema: Trident reference/plumb-v2.md.
// External hook/controller registry proof requests require outer composition.
use os.neptune.standards.plumb
// --- Versioned core and full authority commitment ---
fn hash_leaf(
id: Field, bal: Field, nonce: Field, auth: Digest, lock: Field,
controller: Field, locked_by: Field, lock_data: Field
) -> Digest {
plumb.assert_non_negative(bal)
plumb.assert_non_negative(nonce)
plumb.assert_non_negative(lock)
let core = hash(id,bal,nonce,lock,controller,locked_by,lock_data,2,1,0)
plumb.pair(core,auth)
}
// --- 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()
plumb.assert_non_negative(supply)
let current_time: Field = pub_read()
plumb.assert_non_negative(current_time)
let amount: Field = pub_read()
plumb.assert_non_negative(amount)
let config: Digest = pub_read5()
// --- Verify config ---
let cfg_admin: Digest = divine5()
let cfg_pay_auth: Digest = divine5()
let cfg_lock_auth: Digest = divine5()
let cfg_mint_auth: Digest = divine5()
let cfg_burn_auth: Digest = divine5()
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: Digest = divine5()
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())
plumb.check_index(s_id,s_idx)
// Account-level authorization (always required)
plumb.verify_auth(s_auth)
// Config-level pay auth (0 = self only, else dual)
if plumb.is_zero(cfg_pay_auth) {
} 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: Digest = divine5()
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())
plumb.check_index(r_id,r_idx)
// --- Compute new leaves ---
let new_s_nonce: Field = plumb.next_nonce(s_nonce)
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, plumb.next_nonce(r_nonce), r_auth, r_lock,
r_controller, r_locked_by, r_lock_data
)
// Authenticate new leaves against new state root
assert((s_idx == r_idx) == false)
let intermediate = plumb.update_leaf(s_leaf,new_s_leaf,old_root,s_idx)
assert_digest(plumb.update_leaf(r_leaf,new_r_leaf,intermediate,r_idx),new_root)
// Hook signal
plumb.signal_hook(cfg_pay_hook)
// Nullifier (sealed โ verifier sees commitment)
seal Nullifier { account_id: s_id, nonce: s_nonce }
seal Nullifier { account_id: r_id, nonce: r_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()
plumb.assert_non_negative(supply)
let lock_time: Field = pub_read()
plumb.assert_non_negative(lock_time)
let config: Digest = pub_read5()
// --- Verify config ---
let cfg_admin: Digest = divine5()
let cfg_pay_auth: Digest = divine5()
let cfg_lock_auth: Digest = divine5()
let cfg_mint_auth: Digest = divine5()
let cfg_burn_auth: Digest = divine5()
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: Digest = divine5()
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())
plumb.check_index(a_id,a_idx)
// Account-level authorization (always required)
plumb.verify_auth(a_auth)
// Config-level lock auth (0 = self only, else dual)
if plumb.is_zero(cfg_lock_auth) {
} 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 = plumb.next_nonce(a_nonce)
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
assert_digest(plumb.update_leaf(a_leaf,new_a_leaf,old_root,a_idx),new_root)
// Hook signal
plumb.signal_hook(cfg_lock_hook)
seal Nullifier { account_id: a_id, nonce: a_nonce }
// 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()
plumb.assert_non_negative(supply)
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: Digest = divine5()
let old_pay_auth: Digest = divine5()
let old_lock_auth: Digest = divine5()
let old_mint_auth: Digest = divine5()
let old_burn_auth: Digest = divine5()
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: Digest = divine5()
let new_pay_auth: Digest = divine5()
let new_lock_auth: Digest = divine5()
let new_mint_auth: Digest = divine5()
let new_burn_auth: Digest = divine5()
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()
plumb.assert_non_negative(old_supply)
let new_supply: Field = pub_read()
plumb.assert_non_negative(new_supply)
let amount: Field = pub_read()
plumb.assert_non_negative(amount)
let config: Digest = pub_read5()
// --- Verify config ---
let cfg_admin: Digest = divine5()
let cfg_pay_auth: Digest = divine5()
let cfg_lock_auth: Digest = divine5()
let cfg_mint_auth: Digest = divine5()
let cfg_burn_auth: Digest = divine5()
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: Digest = divine5()
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())
plumb.check_index(r_id,r_idx)
// 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, plumb.next_nonce(r_nonce), r_auth, r_lock,
r_controller, r_locked_by, r_lock_data
)
// Authenticate new leaf against new state root
assert_digest(plumb.update_leaf(r_leaf,new_r_leaf,old_root,r_idx),new_root)
// Hook signal
plumb.signal_hook(cfg_mint_hook)
seal Nullifier { account_id: r_id, nonce: r_nonce }
// 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()
plumb.assert_non_negative(old_supply)
let new_supply: Field = pub_read()
plumb.assert_non_negative(new_supply)
let current_time: Field = pub_read()
plumb.assert_non_negative(current_time)
let amount: Field = pub_read()
plumb.assert_non_negative(amount)
let config: Digest = pub_read5()
// --- Verify config ---
let cfg_admin: Digest = divine5()
let cfg_pay_auth: Digest = divine5()
let cfg_lock_auth: Digest = divine5()
let cfg_mint_auth: Digest = divine5()
let cfg_burn_auth: Digest = divine5()
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: Digest = divine5()
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())
plumb.check_index(a_id,a_idx)
// Account-level authorization (always required)
plumb.verify_auth(a_auth)
// Config-level burn auth (0 = self only, else dual)
if plumb.is_zero(cfg_burn_auth) {
} 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 = plumb.next_nonce(a_nonce)
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
assert_digest(plumb.update_leaf(a_leaf,new_a_leaf,old_root,a_idx),new_root)
// 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()
} else { assert(false) }
}