# Private payments pool (fixed amount) using linkable ring signatures.
#
# 1. deposit(public_key) with exactly DENOMINATION TCN. The public key is a
#    fresh ring key only you control (tccl ring keygen / thecoin-wallet privacy).
# 2. Later, anyone holding the matching secret key withdraws to ANY address by
#    signing with a ring made of several deposited keys. The contract learns
#    that ONE of them signed — never which one. The key image stops the same
#    deposit from being withdrawn twice.
# 3. A relayer can submit the withdrawal and earn `fee`, so the destination
#    address never needs TCN beforehand.
contract PrivatePool

const DENOMINATION: int = 10 * TCN
const MIN_RING: int = 2
const MAX_RING: int = 32

state keys: list[bytes]
state used: map[bytes, bool]
state withdrawn: int

event Deposited(index: int)
event Withdrawn(ring_size: int)

action deposit(public_key: bytes) payable:
    require value == DENOMINATION, "deposit exactly 10 TCN"
    require len(public_key) == 32, "public key must be 32 bytes"
    keys.push(public_key)
    emit Deposited(len(keys) - 1)

action withdraw(to: address, relayer: address, fee: int, members: list[int], signature: bytes, key_image: bytes):
    require len(members) >= MIN_RING and len(members) <= MAX_RING, "ring size must be 2 to 32"
    require fee >= 0 and fee < DENOMINATION, "invalid relayer fee"
    require not used.has(key_image), "this deposit was already withdrawn"
    let ring: list[bytes] = []
    for i in members:
        require i >= 0 and i < len(keys), "unknown deposit index"
        ring.push(keys[i])
    let message: bytes = withdraw_message(to, relayer, fee)
    require ring_verify(ring, message, signature, key_image), "invalid ring signature"
    used[key_image] = true
    withdrawn += 1
    send(to, DENOMINATION - fee)
    if fee > 0:
        send(relayer, fee)
    emit Withdrawn(len(members))

# Standard privacy pool interface (TCCL-PRIV-1) used by wallets:
# denomination, deposits, key_at, is_withdrawn, message_for, deposit, withdraw.
view denomination() -> int:
    return DENOMINATION

view deposits() -> int:
    return len(keys)

view is_withdrawn(key_image: bytes) -> bool:
    return used.has(key_image)

view key_at(index: int) -> bytes:
    return keys[index]

view message_for(to: address, relayer: address, fee: int) -> bytes:
    return withdraw_message(to, relayer, fee)

fn withdraw_message(to: address, relayer: address, fee: int) -> bytes:
    return blake3(to_bytes(self) + to_bytes(to) + to_bytes(relayer) + to_bytes(fee))
